@khanacademy/perseus 46.0.1 → 47.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -649,7 +649,7 @@ class InnerMathInput extends React__namespace.Component {
649
649
  className = className + " " + this.props.className;
650
650
  }
651
651
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
652
- style: [styles$E.outerWrapper, this.state.focused && styles$E.wrapperFocused, this.props.hasError && styles$E.wrapperError]
652
+ style: [styles$D.outerWrapper, this.state.focused && styles$D.wrapperFocused, this.props.hasError && styles$D.wrapperError]
653
653
  }, /*#__PURE__*/React__namespace.createElement("div", {
654
654
  style: {
655
655
  display: "flex",
@@ -685,7 +685,7 @@ class InnerMathInput extends React__namespace.Component {
685
685
  style: a11y.srOnly
686
686
  }, this.context.strings.mathInputDescription), /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.PopoverContentCore, {
687
687
  closeButtonVisible: true,
688
- style: styles$E.popoverContent
688
+ style: styles$D.popoverContent
689
689
  }, /*#__PURE__*/React__namespace.createElement(mathInput.DesktopKeypad, _extends({
690
690
  onAnalyticsEvent: this.props.onAnalyticsEvent,
691
691
  extraKeys: this.props.extraKeys,
@@ -750,9 +750,9 @@ const MathInputIcon = _ref => {
750
750
  fillColor = wonderBlocksTokens.color.offBlack;
751
751
  break;
752
752
  }
753
- const dynamicClass = active || focused ? styles$E.iconActive : styles$E.iconInactive;
753
+ const dynamicClass = active || focused ? styles$D.iconActive : styles$D.iconInactive;
754
754
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
755
- style: [styles$E.iconContainer, dynamicClass]
755
+ style: [styles$D.iconContainer, dynamicClass]
756
756
  }, /*#__PURE__*/React__namespace.createElement("svg", {
757
757
  xmlns: "http://www.w3.org/2000/svg",
758
758
  width: "16",
@@ -797,7 +797,7 @@ const inputFocused = {
797
797
  borderColor: wonderBlocksTokens.color.blue,
798
798
  margin: -1
799
799
  };
800
- const styles$E = aphrodite.StyleSheet.create({
800
+ const styles$D = aphrodite.StyleSheet.create({
801
801
  iconContainer: {
802
802
  display: "flex",
803
803
  justifyContent: "center",
@@ -2603,7 +2603,7 @@ class Expression extends React__namespace.Component {
2603
2603
  render() {
2604
2604
  if (this.props.apiOptions.customKeypad) {
2605
2605
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2606
- className: aphrodite.css(styles$D.mobileLabelInputWrapper)
2606
+ className: aphrodite.css(styles$C.mobileLabelInputWrapper)
2607
2607
  }, !!this.props.visibleLabel && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, {
2608
2608
  htmlFor: this._textareaId,
2609
2609
  tag: "label"
@@ -2637,7 +2637,7 @@ class Expression extends React__namespace.Component {
2637
2637
  ERROR_TITLE
2638
2638
  } = this.context.strings;
2639
2639
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2640
- className: aphrodite.css(styles$D.desktopLabelInputWrapper)
2640
+ className: aphrodite.css(styles$C.desktopLabelInputWrapper)
2641
2641
  }, !!this.props.visibleLabel && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, {
2642
2642
  htmlFor: this._textareaId,
2643
2643
  tag: "label"
@@ -2676,7 +2676,7 @@ class Expression extends React__namespace.Component {
2676
2676
  }))));
2677
2677
  }
2678
2678
  }
2679
- const styles$D = aphrodite.StyleSheet.create({
2679
+ const styles$C = aphrodite.StyleSheet.create({
2680
2680
  mobileLabelInputWrapper: {
2681
2681
  padding: "15px 4px 0"
2682
2682
  },
@@ -18124,33 +18124,33 @@ class Lint extends React__namespace.Component {
18124
18124
  let warningText;
18125
18125
  let warningTextStyle;
18126
18126
  if (this.props.severity === Severity.Error) {
18127
- severityStyle = styles$C.indicatorError;
18127
+ severityStyle = styles$B.indicatorError;
18128
18128
  warningText = "Error";
18129
- warningTextStyle = styles$C.publishBlockingError;
18129
+ warningTextStyle = styles$B.publishBlockingError;
18130
18130
  } else if (this.props.severity === Severity.Warning) {
18131
- severityStyle = styles$C.indicatorWarning;
18131
+ severityStyle = styles$B.indicatorWarning;
18132
18132
  warningText = "Warning";
18133
- warningTextStyle = styles$C.warning;
18133
+ warningTextStyle = styles$B.warning;
18134
18134
  } else {
18135
- severityStyle = styles$C.indicatorGuideline;
18135
+ severityStyle = styles$B.indicatorGuideline;
18136
18136
  warningText = "Recommendation";
18137
- warningTextStyle = styles$C.warning;
18137
+ warningTextStyle = styles$B.warning;
18138
18138
  }
18139
18139
  return /*#__PURE__*/React__namespace.createElement("a", {
18140
18140
  href: `https://khanacademy.org/r/linter-rules#${this.props.ruleName}`,
18141
18141
  target: "lint-help-window",
18142
18142
  className: aphrodite.css(style)
18143
18143
  }, /*#__PURE__*/React__namespace.createElement("span", {
18144
- className: aphrodite.css(styles$C.indicator, severityStyle)
18144
+ className: aphrodite.css(styles$B.indicator, severityStyle)
18145
18145
  }, this.props.severity === 1 && /*#__PURE__*/React__namespace.createElement(InlineIcon, exclamationIcon)), /*#__PURE__*/React__namespace.createElement("div", {
18146
- className: aphrodite.css(styles$C.tooltip, tooltipAbove && styles$C.tooltipAbove)
18146
+ className: aphrodite.css(styles$B.tooltip, tooltipAbove && styles$B.tooltipAbove)
18147
18147
  }, this.props.message.split("\n\n").map((m, i) => /*#__PURE__*/React__namespace.createElement("p", {
18148
18148
  key: i,
18149
- className: aphrodite.css(styles$C.tooltipParagraph)
18149
+ className: aphrodite.css(styles$B.tooltipParagraph)
18150
18150
  }, /*#__PURE__*/React__namespace.createElement("span", {
18151
18151
  className: aphrodite.css(warningTextStyle)
18152
18152
  }, warningText, ":", " "), m)), /*#__PURE__*/React__namespace.createElement("div", {
18153
- className: aphrodite.css(styles$C.tail, tooltipAbove && styles$C.tailAbove)
18153
+ className: aphrodite.css(styles$B.tail, tooltipAbove && styles$B.tailAbove)
18154
18154
  })));
18155
18155
  };
18156
18156
 
@@ -18178,20 +18178,20 @@ class Lint extends React__namespace.Component {
18178
18178
  }
18179
18179
  if (blockHighlight) {
18180
18180
  return /*#__PURE__*/React__namespace.createElement("span", {
18181
- className: aphrodite.css(styles$C.lintContainer, styles$C.lintContainerBlock)
18182
- }, this.renderLink(styles$C.radioWidgetHoverTarget), /*#__PURE__*/React__namespace.createElement("span", null, children));
18181
+ className: aphrodite.css(styles$B.lintContainer, styles$B.lintContainerBlock)
18182
+ }, this.renderLink(styles$B.radioWidgetHoverTarget), /*#__PURE__*/React__namespace.createElement("span", null, children));
18183
18183
  }
18184
18184
  if (inline) {
18185
18185
  return /*#__PURE__*/React__namespace.createElement("span", {
18186
- className: aphrodite.css(styles$C.lintContainer)
18187
- }, this.renderLink(styles$C.inlineHoverTarget), /*#__PURE__*/React__namespace.createElement("span", null, children));
18186
+ className: aphrodite.css(styles$B.lintContainer)
18187
+ }, this.renderLink(styles$B.inlineHoverTarget), /*#__PURE__*/React__namespace.createElement("span", null, children));
18188
18188
  }
18189
18189
  return /*#__PURE__*/React__namespace.createElement("div", {
18190
- className: aphrodite.css(styles$C.lintContainer)
18191
- }, this.renderLink(styles$C.hoverTarget), /*#__PURE__*/React__namespace.createElement("div", null, children));
18190
+ className: aphrodite.css(styles$B.lintContainer)
18191
+ }, this.renderLink(styles$B.hoverTarget), /*#__PURE__*/React__namespace.createElement("div", null, children));
18192
18192
  }
18193
18193
  }
18194
- const styles$C = aphrodite.StyleSheet.create({
18194
+ const styles$B = aphrodite.StyleSheet.create({
18195
18195
  // This is the class of the outermost element.
18196
18196
  // We use relative positioning so that the lint indicator can be
18197
18197
  // positioned absolutely relative to the position of the linty container.
@@ -19012,8 +19012,6 @@ function getWidgetFromWidgetMap(widgetId, widgetMap) {
19012
19012
 
19013
19013
  /**
19014
19014
  * Get select widgets from a widget map.
19015
- * Useful for multi-items that needs to split
19016
- * one PerseusItem into several
19017
19015
  *
19018
19016
  * @param {ReadonlyArray<string>} widgetIds to extract
19019
19017
  * @param {PerseusWidgetsMap} widgetMap to extract from
@@ -20618,7 +20616,7 @@ class Renderer extends React__namespace.Component {
20618
20616
  };
20619
20617
  const mobileInnerStyle = {
20620
20618
  ...innerStyle,
20621
- ...styles$B.mobileZoomableParentFix
20619
+ ...styles$A.mobileZoomableParentFix
20622
20620
  };
20623
20621
  return /*#__PURE__*/React__namespace.createElement("div", {
20624
20622
  key: state.key,
@@ -20758,7 +20756,7 @@ class Renderer extends React__namespace.Component {
20758
20756
  };
20759
20757
  const mobileInnerStyle = {
20760
20758
  ...innerStyle,
20761
- ...styles$B.mobileZoomableParentFix
20759
+ ...styles$A.mobileZoomableParentFix
20762
20760
  };
20763
20761
  const wrappedOutput = /*#__PURE__*/React__namespace.createElement("div", {
20764
20762
  style: {
@@ -21221,7 +21219,7 @@ class Renderer extends React__namespace.Component {
21221
21219
  return this.lastRenderedMarkdown;
21222
21220
  }
21223
21221
  }
21224
- const styles$B = {
21222
+ const styles$A = {
21225
21223
  mobileZoomableParentFix: {
21226
21224
  // NOTE(abdul): There is an issue where transform animations will
21227
21225
  // cause the Zoomable component to disappear when running on the
@@ -22187,10 +22185,10 @@ class InputNumber extends React__namespace.Component {
22187
22185
  // Note: This is _very_ similar to what `numeric-input.jsx` does. If
22188
22186
  // you modify this, double-check if you also need to modify that
22189
22187
  // component.
22190
- const inputStyles = [styles$A.default, this.props.size === "small" ? styles$A.small : null, this.props.rightAlign ? styles$A.rightAlign : styles$A.leftAlign];
22188
+ const inputStyles = [styles$z.default, this.props.size === "small" ? styles$z.small : null, this.props.rightAlign ? styles$z.rightAlign : styles$z.leftAlign];
22191
22189
  // Unanswered
22192
22190
  if (this.props.reviewMode && !this.props.currentValue) {
22193
- inputStyles.push(styles$A.answerStateUnanswered);
22191
+ inputStyles.push(styles$z.answerStateUnanswered);
22194
22192
  }
22195
22193
  return /*#__PURE__*/React__namespace.createElement(InputWithExamples
22196
22194
  // eslint-disable-next-line react/no-string-refs
@@ -22209,7 +22207,7 @@ class InputNumber extends React__namespace.Component {
22209
22207
  });
22210
22208
  }
22211
22209
  }
22212
- const styles$A = aphrodite.StyleSheet.create({
22210
+ const styles$z = aphrodite.StyleSheet.create({
22213
22211
  default: {
22214
22212
  width: 80,
22215
22213
  height: "auto"
@@ -23377,7 +23375,7 @@ class HighlightRenderer extends React__namespace.PureComponent {
23377
23375
  const rects = this.state.cachedHighlightRects;
23378
23376
  return /*#__PURE__*/React__namespace.createElement("div", null, rects.map((rect, index) => /*#__PURE__*/React__namespace.createElement("div", {
23379
23377
  key: index,
23380
- className: aphrodite.css(styles$z.highlightRect),
23378
+ className: aphrodite.css(styles$y.highlightRect),
23381
23379
  style: {
23382
23380
  // NOTE(mdr): We apply `position: absolute` here
23383
23381
  // rather than in Aphrodite styles, because
@@ -23396,7 +23394,7 @@ class HighlightRenderer extends React__namespace.PureComponent {
23396
23394
  })));
23397
23395
  }
23398
23396
  }
23399
- const styles$z = aphrodite.StyleSheet.create({
23397
+ const styles$y = aphrodite.StyleSheet.create({
23400
23398
  highlightRect: {
23401
23399
  background: "#fffabe" // highlighter yellow :)
23402
23400
  }
@@ -23477,7 +23475,7 @@ class HighlightTooltip extends React__namespace.PureComponent {
23477
23475
  // TooltipContent wouldn't let me overwrite
23478
23476
  // user-select and onClick
23479
23477
  const content = /*#__PURE__*/React__namespace.createElement("div", {
23480
- className: aphrodite.css(styles$y.tooltipLabel),
23478
+ className: aphrodite.css(styles$x.tooltipLabel),
23481
23479
  onClick: this.props.onClick
23482
23480
  }, this.props.label);
23483
23481
  const style = {
@@ -23497,7 +23495,7 @@ class HighlightTooltip extends React__namespace.PureComponent {
23497
23495
  }, /*#__PURE__*/React__namespace.createElement("div", null)));
23498
23496
  }
23499
23497
  }
23500
- const styles$y = aphrodite.StyleSheet.create({
23498
+ const styles$x = aphrodite.StyleSheet.create({
23501
23499
  tooltipLabel: {
23502
23500
  // Unlike most tooltip contents, we don't want this label to be
23503
23501
  // selectable. Selecting it is not only unhelpful, but dangerous: if
@@ -24055,7 +24053,7 @@ class HighlightableContent extends React__namespace.PureComponent {
24055
24053
  // starting point: https://phabricator.khanacademy.org/D35623?id=170698
24056
24054
  const buildHighlight = r => this._buildHighlight(r);
24057
24055
  return /*#__PURE__*/React__namespace.createElement("div", {
24058
- className: aphrodite.css(styles$x.container),
24056
+ className: aphrodite.css(styles$w.container),
24059
24057
  ref: container => this._container = container
24060
24058
  }, /*#__PURE__*/React__namespace.createElement("div", null, this.props.enabled && this._container && this._content && /*#__PURE__*/React__namespace.createElement(HighlightingUI, {
24061
24059
  buildHighlight: buildHighlight,
@@ -24076,14 +24074,14 @@ class HighlightableContent extends React__namespace.PureComponent {
24076
24074
  onAddHighlight: this._handleAddHighlight,
24077
24075
  onRemoveHighlight: this._handleRemoveHighlight
24078
24076
  })), /*#__PURE__*/React__namespace.createElement("div", {
24079
- className: aphrodite.css(styles$x.content),
24077
+ className: aphrodite.css(styles$w.content),
24080
24078
  ref: content => this._content = content
24081
24079
  }, /*#__PURE__*/React__namespace.createElement(WordIndexer, {
24082
24080
  onWordsUpdate: this._handleWordsUpdate
24083
24081
  }, this.props.children)));
24084
24082
  }
24085
24083
  }
24086
- const styles$x = aphrodite.StyleSheet.create({
24084
+ const styles$w = aphrodite.StyleSheet.create({
24087
24085
  // We render content with z-index 1, and highlights with z-index 0, so that
24088
24086
  // content will receive the browser's native pointer events. (Highlights
24089
24087
  // have their own magic code to manage hover detection, despite being
@@ -24505,7 +24503,7 @@ class LineHeightMeasurer extends React__namespace.Component {
24505
24503
  }
24506
24504
  render() {
24507
24505
  return /*#__PURE__*/React__namespace.createElement("div", {
24508
- className: aphrodite.css(styles$w.measurer),
24506
+ className: aphrodite.css(styles$v.measurer),
24509
24507
  "aria-hidden": "true"
24510
24508
  }, /*#__PURE__*/React__namespace.createElement("div", {
24511
24509
  ref: ref => this._line = ref,
@@ -24513,7 +24511,7 @@ class LineHeightMeasurer extends React__namespace.Component {
24513
24511
  }, "Line Height Measurement"));
24514
24512
  }
24515
24513
  }
24516
- const styles$w = aphrodite.StyleSheet.create({
24514
+ const styles$v = aphrodite.StyleSheet.create({
24517
24515
  measurer: {
24518
24516
  position: "absolute",
24519
24517
  top: 0,
@@ -25494,11 +25492,11 @@ const FocusRing = function (_ref) {
25494
25492
  };
25495
25493
  return /*#__PURE__*/React__namespace.createElement("span", {
25496
25494
  "data-testid": "focus-ring",
25497
- className: aphrodite.css(styles$v.ring),
25495
+ className: aphrodite.css(styles$u.ring),
25498
25496
  style: style
25499
25497
  }, children);
25500
25498
  };
25501
- const styles$v = aphrodite.StyleSheet.create({
25499
+ const styles$u = aphrodite.StyleSheet.create({
25502
25500
  ring: {
25503
25501
  margin: "auto",
25504
25502
  display: "inline-block",
@@ -25525,7 +25523,7 @@ function getChoiceLetter(pos, strings) {
25525
25523
  }
25526
25524
 
25527
25525
  const CHOICE_ICON_SIZE = 24;
25528
- const styles$u = aphrodite.StyleSheet.create({
25526
+ const styles$t = aphrodite.StyleSheet.create({
25529
25527
  iconWrapper: {
25530
25528
  display: "inline-block",
25531
25529
  position: "relative"
@@ -25548,7 +25546,7 @@ const CrossOutLine = function (props) {
25548
25546
  width: CROSS_OUT_LINE_SIZE,
25549
25547
  height: CROSS_OUT_LINE_SIZE,
25550
25548
  viewBox: `0 0 ${CROSS_OUT_LINE_SIZE} ${CROSS_OUT_LINE_SIZE}`,
25551
- className: aphrodite.css(styles$t.crossOutLine),
25549
+ className: aphrodite.css(styles$s.crossOutLine),
25552
25550
  "data-testid": "choice-icon__cross-out-line"
25553
25551
  }, /*#__PURE__*/React__namespace.createElement("line", {
25554
25552
  // The line stretches from the bottom-left to top-right.
@@ -25563,7 +25561,7 @@ const CrossOutLine = function (props) {
25563
25561
  strokeLinecap: "round"
25564
25562
  }));
25565
25563
  };
25566
- const styles$t = aphrodite.StyleSheet.create({
25564
+ const styles$s = aphrodite.StyleSheet.create({
25567
25565
  crossOutLine: {
25568
25566
  // Center the icon within the container.
25569
25567
  position: "absolute",
@@ -25647,7 +25645,7 @@ const ChoiceIcon = function (props) {
25647
25645
  } = props;
25648
25646
  const dynamicStyles = getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct);
25649
25647
  return /*#__PURE__*/React__namespace.createElement("div", {
25650
- className: aphrodite.css(styles$u.iconWrapper)
25648
+ className: aphrodite.css(styles$t.iconWrapper)
25651
25649
  }, /*#__PURE__*/React__namespace.createElement(FocusRing, {
25652
25650
  color: wonderBlocksTokens.color.blue,
25653
25651
  visible: focused || hovered,
@@ -25655,13 +25653,13 @@ const ChoiceIcon = function (props) {
25655
25653
  }, /*#__PURE__*/React__namespace.createElement("div", {
25656
25654
  style: dynamicStyles,
25657
25655
  "data-testid": "choice-icon__library-choice-icon",
25658
- className: aphrodite.css(styles$s.circle, showCorrectness && correct && styles$s.circleCorrect, showCorrectness && !correct && styles$s.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$s.circleIncorrectAnswered)
25656
+ className: aphrodite.css(styles$r.circle, showCorrectness && correct && styles$r.circleCorrect, showCorrectness && !correct && styles$r.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$r.circleIncorrectAnswered)
25659
25657
  // used in BaseRadio editMode to check
25660
25658
  // if we actually clicked on the radio icon
25661
25659
  ,
25662
25660
  "data-is-radio-icon": true
25663
25661
  }, /*#__PURE__*/React__namespace.createElement("div", {
25664
- className: aphrodite.css(styles$s.innerWrapper)
25662
+ className: aphrodite.css(styles$r.innerWrapper)
25665
25663
  }, /*#__PURE__*/React__namespace.createElement(ChoiceInner, {
25666
25664
  pos: pos,
25667
25665
  showCorrectness: showCorrectness,
@@ -25670,7 +25668,7 @@ const ChoiceIcon = function (props) {
25670
25668
  color: dynamicStyles.borderColor
25671
25669
  }));
25672
25670
  };
25673
- const styles$s = aphrodite.StyleSheet.create({
25671
+ const styles$r = aphrodite.StyleSheet.create({
25674
25672
  innerWrapper: {
25675
25673
  width: CHOICE_ICON_SIZE,
25676
25674
  height: CHOICE_ICON_SIZE,
@@ -25765,19 +25763,19 @@ const OptionStatus = function (props) {
25765
25763
  }
25766
25764
  let textStyle;
25767
25765
  if (correct) {
25768
- textStyle = styles$r.correct;
25766
+ textStyle = styles$q.correct;
25769
25767
  } else {
25770
25768
  if (checked || previouslyAnswered) {
25771
- textStyle = styles$r.incorrectAnswered;
25769
+ textStyle = styles$q.incorrectAnswered;
25772
25770
  } else {
25773
- textStyle = styles$r.incorrect;
25771
+ textStyle = styles$q.incorrect;
25774
25772
  }
25775
25773
  }
25776
25774
  return /*#__PURE__*/React__namespace.createElement("div", {
25777
- className: aphrodite.css(styles$r.text, textStyle)
25775
+ className: aphrodite.css(styles$q.text, textStyle)
25778
25776
  }, renderText(checked, correct, crossedOut, strings));
25779
25777
  };
25780
- const styles$r = aphrodite.StyleSheet.create({
25778
+ const styles$q = aphrodite.StyleSheet.create({
25781
25779
  text: {
25782
25780
  alignItems: "center",
25783
25781
  display: "flex",
@@ -25846,8 +25844,8 @@ const Choice = function (props) {
25846
25844
  crossedOut: updatedCrossedOut
25847
25845
  });
25848
25846
  }
25849
- const descriptionClassName = classNames__default["default"]("description", aphrodite.css(styles$q.description));
25850
- const rationaleClassName = classNames__default["default"]("perseus-radio-rationale-content", aphrodite.css(styles$q.rationale));
25847
+ const descriptionClassName = classNames__default["default"]("description", aphrodite.css(styles$p.description));
25848
+ const rationaleClassName = classNames__default["default"]("perseus-radio-rationale-content", aphrodite.css(styles$p.rationale));
25851
25849
 
25852
25850
  // We want to show the choices as dimmed out when the choices are disabled.
25853
25851
  // However, we don't want to do this when we're in review mode in the
@@ -26014,7 +26012,7 @@ const Choice = function (props) {
26014
26012
  "data-testid": `perseus-radio-rationale-content-${pos}`
26015
26013
  }, rationale));
26016
26014
  };
26017
- const styles$q = aphrodite.StyleSheet.create({
26015
+ const styles$p = aphrodite.StyleSheet.create({
26018
26016
  description: {
26019
26017
  display: "inline-block",
26020
26018
  width: "100%"
@@ -26205,10 +26203,10 @@ const BaseRadio = function (_ref) {
26205
26203
  const isMobile = apiOptions.isMobile;
26206
26204
  const firstChoiceHighlighted = choices[0].highlighted;
26207
26205
  const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
26208
- const className = classNames__default["default"]("perseus-widget-radio", !editMode && "perseus-rendered-radio", aphrodite.css(styles$p.radio, styles$p.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$p.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$p.radioContainerLastHighlighted));
26209
- const instructionsClassName = classNames__default["default"]("instructions", aphrodite.css(styles$p.instructions, isMobile && styles$p.instructionsMobile));
26206
+ const className = classNames__default["default"]("perseus-widget-radio", !editMode && "perseus-rendered-radio", aphrodite.css(styles$o.radio, styles$o.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$o.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$o.radioContainerLastHighlighted));
26207
+ const instructionsClassName = classNames__default["default"]("instructions", aphrodite.css(styles$o.instructions, isMobile && styles$o.instructionsMobile));
26210
26208
  const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect, strings);
26211
- const responsiveClassName = aphrodite.css(styles$p.responsiveFieldset);
26209
+ const responsiveClassName = aphrodite.css(styles$o.responsiveFieldset);
26212
26210
  const fieldset = /*#__PURE__*/React__namespace.createElement("fieldset", {
26213
26211
  className: `perseus-widget-radio-fieldset ${responsiveClassName}`
26214
26212
  }, /*#__PURE__*/React__namespace.createElement("legend", {
@@ -26259,7 +26257,7 @@ const BaseRadio = function (_ref) {
26259
26257
  const aphroditeClassName = checked => {
26260
26258
  // Whether or not to show correctness borders
26261
26259
  // for this choice and the next choice.
26262
- return aphrodite.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);
26260
+ return aphrodite.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);
26263
26261
  };
26264
26262
 
26265
26263
  // HACK(abdulrahman): Preloads the selection-state
@@ -26324,10 +26322,10 @@ const BaseRadio = function (_ref) {
26324
26322
  // Allow for horizontal scrolling if content is too wide, which may be
26325
26323
  // an issue especially on phones.
26326
26324
  return /*#__PURE__*/React__namespace.createElement("div", {
26327
- className: aphrodite.css(styles$p.responsiveContainer)
26325
+ className: aphrodite.css(styles$o.responsiveContainer)
26328
26326
  }, fieldset);
26329
26327
  };
26330
- const styles$p = aphrodite.StyleSheet.create({
26328
+ const styles$o = aphrodite.StyleSheet.create({
26331
26329
  instructions: {
26332
26330
  display: "block",
26333
26331
  color: gray17,
@@ -27059,9 +27057,9 @@ class Categorizer extends React__namespace.Component {
27059
27057
  indexedItems = Util.shuffle(indexedItems, this.props.problemNum);
27060
27058
  }
27061
27059
  const table = /*#__PURE__*/React__namespace.createElement("table", {
27062
- className: "categorizer-table " + aphrodite.css(styles$o.mobileTable)
27060
+ className: "categorizer-table " + aphrodite.css(styles$n.mobileTable)
27063
27061
  }, /*#__PURE__*/React__namespace.createElement("thead", null, /*#__PURE__*/React__namespace.createElement("tr", null, /*#__PURE__*/React__namespace.createElement("td", {
27064
- className: aphrodite.css(styles$o.emptyHeaderCell)
27062
+ className: aphrodite.css(styles$n.emptyHeaderCell)
27065
27063
  }), this.props.categories.map((category, i) => {
27066
27064
  // Array index is the correct key here, as that's
27067
27065
  // how category grading actually works -- no way
@@ -27069,7 +27067,7 @@ class Categorizer extends React__namespace.Component {
27069
27067
  // middle. (If we later add that, this should be
27070
27068
  // fixed.)
27071
27069
  return /*#__PURE__*/React__namespace.createElement("th", {
27072
- className: aphrodite.css(styles$o.header),
27070
+ className: aphrodite.css(styles$n.header),
27073
27071
  key: i
27074
27072
  }, /*#__PURE__*/React__namespace.createElement(Renderer, {
27075
27073
  content: category,
@@ -27091,7 +27089,7 @@ class Categorizer extends React__namespace.Component {
27091
27089
  })), self.props.categories.map((catName, catNum) => {
27092
27090
  const selected = self.props.values[itemNum] === catNum;
27093
27091
  return /*#__PURE__*/React__namespace.createElement("td", {
27094
- className: "category " + aphrodite.css(styles$o.cell),
27092
+ className: "category " + aphrodite.css(styles$n.cell),
27095
27093
  key: catNum
27096
27094
  }, /*#__PURE__*/React__namespace.createElement("div", {
27097
27095
  className: ClassNames.INTERACTIVE,
@@ -27106,7 +27104,7 @@ class Categorizer extends React__namespace.Component {
27106
27104
  onChange: () => this.onChange(itemNum, catNum),
27107
27105
  onClick: e => e.stopPropagation()
27108
27106
  }), !isMobile && /*#__PURE__*/React__namespace.createElement("span", {
27109
- className: aphrodite.css(styles$o.radioSpan, selected && styles$o.checkedRadioSpan, this.props.static && selected && styles$o.staticCheckedRadioSpan)
27107
+ className: aphrodite.css(styles$n.radioSpan, selected && styles$n.checkedRadioSpan, this.props.static && selected && styles$n.staticCheckedRadioSpan)
27110
27108
  }, selected ? /*#__PURE__*/React__namespace.createElement(InlineIcon, iconCircle) : /*#__PURE__*/React__namespace.createElement(InlineIcon, iconCircleThin))));
27111
27109
  }));
27112
27110
  /* eslint-enable max-len */
@@ -27118,7 +27116,7 @@ class Categorizer extends React__namespace.Component {
27118
27116
  "categorizer-container": true,
27119
27117
  "static-mode": this.props.static
27120
27118
  });
27121
- const inlineStyles = this.props.apiOptions.isMobile ? [styles$o.fullBleedContainer] : [];
27119
+ const inlineStyles = this.props.apiOptions.isMobile ? [styles$n.fullBleedContainer] : [];
27122
27120
  return /*#__PURE__*/React__namespace.createElement("div", {
27123
27121
  className: extraClassNames + " " + aphrodite.css(...inlineStyles)
27124
27122
  }, table);
@@ -27130,7 +27128,7 @@ const pageMargin = 16;
27130
27128
 
27131
27129
  // Stylesheets aren't directly testable
27132
27130
  /* c8 ignore next */
27133
- const styles$o = aphrodite.StyleSheet.create({
27131
+ const styles$n = aphrodite.StyleSheet.create({
27134
27132
  mobileTable: {
27135
27133
  [mediaQueries.smOrSmaller]: {
27136
27134
  minWidth: "auto"
@@ -27399,7 +27397,7 @@ class CSProgram extends React__namespace.Component {
27399
27397
  // creator "went wild".
27400
27398
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
27401
27399
  return /*#__PURE__*/React__namespace.createElement("div", {
27402
- className: aphrodite.css(styles$n.widthOverride, styleContainer && styles$n.container)
27400
+ className: aphrodite.css(styles$m.widthOverride, styleContainer && styles$m.container)
27403
27401
  }, /*#__PURE__*/React__namespace.createElement("iframe", {
27404
27402
  sandbox: sandboxOptions,
27405
27403
  src: url,
@@ -27409,7 +27407,7 @@ class CSProgram extends React__namespace.Component {
27409
27407
  }));
27410
27408
  }
27411
27409
  }
27412
- const styles$n = aphrodite.StyleSheet.create({
27410
+ const styles$m = aphrodite.StyleSheet.create({
27413
27411
  // Override the inherited width from the perseus paragraph class
27414
27412
  // 820 is minimum width for 420px editor and 400px canvas
27415
27413
  widthOverride: {
@@ -27460,7 +27458,7 @@ class Definition extends React__namespace.Component {
27460
27458
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.Popover, {
27461
27459
  content: /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.PopoverContentCore, {
27462
27460
  color: "white",
27463
- style: styles$m.tooltipBody,
27461
+ style: styles$l.tooltipBody,
27464
27462
  closeButtonVisible: true
27465
27463
  }, /*#__PURE__*/React__namespace.createElement(Renderer, {
27466
27464
  apiOptions: this.props.apiOptions,
@@ -27492,7 +27490,7 @@ class Definition extends React__namespace.Component {
27492
27490
  });
27493
27491
  }
27494
27492
  }
27495
- const styles$m = {
27493
+ const styles$l = {
27496
27494
  tooltipBody: {
27497
27495
  color: wonderBlocksTokens.color.offBlack,
27498
27496
  fontSize: 20,
@@ -27618,7 +27616,7 @@ class Dropdown extends React__namespace.Component {
27618
27616
  key: "placeholder",
27619
27617
  value: "0",
27620
27618
  disabled: true,
27621
- label: this.props.placeholder
27619
+ label: this.props.placeholder || " "
27622
27620
  }), ...this.props.choices.map((choice, i) => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
27623
27621
  key: String(i + 1),
27624
27622
  value: String(i + 1),
@@ -27639,15 +27637,14 @@ class Dropdown extends React__namespace.Component {
27639
27637
  }
27640
27638
  }, this.props.visibleLabel && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, {
27641
27639
  tag: "label",
27642
- id: ids.get("dropdown-label")
27640
+ htmlFor: ids.get("dropdown")
27643
27641
  }, this.props.visibleLabel), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
27644
27642
  id: ids.get("dropdown"),
27645
27643
  placeholder: "",
27646
27644
  onChange: value => this._handleChange(parseInt(value)),
27647
27645
  selectedValue: String(this.props.selected),
27648
27646
  disabled: this.props.apiOptions.readOnly,
27649
- "aria-label": this.props.ariaLabel || this.context.strings.selectAnAnswer,
27650
- "aria-labelledby": ids.get("dropdown-label")
27647
+ "aria-label": this.props.ariaLabel || this.props.visibleLabel || this.context.strings.selectAnAnswer
27651
27648
  // This is currently necessary for SRs to read the labels properly.
27652
27649
  // However, WB is working on a change to add the "combobox" role to
27653
27650
  // all dropdowns.
@@ -27761,7 +27758,7 @@ class Explanation extends React__namespace.Component {
27761
27758
  marginRight: "-6px",
27762
27759
  "white-space": "normal"
27763
27760
  };
27764
- const contentStyling = [styles$l.content, this.state.expanded ? styles$l.contentExpanded : styles$l.contentCollapsed, allowTransition && (this.state.expanded ? styles$l.transitionExpanded : styles$l.transitionCollapsed)];
27761
+ const contentStyling = [styles$k.content, this.state.expanded ? styles$k.contentExpanded : styles$k.contentCollapsed, allowTransition && (this.state.expanded ? styles$k.transitionExpanded : styles$k.transitionCollapsed)];
27765
27762
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.UniqueIDProvider, {
27766
27763
  mockOnFirstRender: true,
27767
27764
  scope: "explanation-widget"
@@ -27780,7 +27777,7 @@ class Explanation extends React__namespace.Component {
27780
27777
  "aria-hidden": !this.state.expanded,
27781
27778
  testId: "content-container"
27782
27779
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
27783
- style: styles$l.contentWrapper
27780
+ style: styles$k.contentWrapper
27784
27781
  }, /*#__PURE__*/React__namespace.createElement(Renderer, {
27785
27782
  apiOptions: this.props.apiOptions,
27786
27783
  content: this.props.explanation,
@@ -27793,7 +27790,7 @@ class Explanation extends React__namespace.Component {
27793
27790
  const leftBorderSpacing = 23;
27794
27791
  const verticalContentPadding = 10;
27795
27792
  const arrowHeight = 14;
27796
- const styles$l = aphrodite.StyleSheet.create({
27793
+ const styles$k = aphrodite.StyleSheet.create({
27797
27794
  content: {
27798
27795
  borderLeft: "0px solid #ccc",
27799
27796
  display: "inline-grid",
@@ -27879,15 +27876,15 @@ class GradedGroupAnswerBar extends React__namespace.Component {
27879
27876
  nextQuestion
27880
27877
  } = this.context.strings;
27881
27878
  const answerBarStyle = {
27882
- ...styles$k.answerBar,
27879
+ ...styles$j.answerBar,
27883
27880
  backgroundColor: answerBarState === "CORRECT" ? wonderBlocksTokens.color.offWhite : "white",
27884
27881
  // Center the "Correct!" message only when there's no next question
27885
27882
  justifyContent: answerBarState === "CORRECT" && !onNextQuestion ? "center" : "space-between"
27886
27883
  };
27887
27884
  const message = answerBarState === "INCORRECT" ? /*#__PURE__*/React__namespace.createElement("span", {
27888
- style: styles$k.text
27885
+ style: styles$j.text
27889
27886
  }, /*#__PURE__*/React__namespace.createElement("span", {
27890
- style: styles$k.tryAgainIcon
27887
+ style: styles$j.tryAgainIcon
27891
27888
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon, iconTryAgain)), /*#__PURE__*/React__namespace.createElement("span", {
27892
27889
  style: {
27893
27890
  marginLeft: 8
@@ -27906,7 +27903,7 @@ class GradedGroupAnswerBar extends React__namespace.Component {
27906
27903
  return /*#__PURE__*/React__namespace.createElement("div", {
27907
27904
  style: answerBarStyle
27908
27905
  }, /*#__PURE__*/React__namespace.createElement("span", {
27909
- style: styles$k.text
27906
+ style: styles$j.text
27910
27907
  }, /*#__PURE__*/React__namespace.createElement("span", {
27911
27908
  style: {
27912
27909
  fontSize: 28,
@@ -27928,7 +27925,7 @@ class GradedGroupAnswerBar extends React__namespace.Component {
27928
27925
  }
27929
27926
  }
27930
27927
  const fontSize = 17;
27931
- const styles$k = {
27928
+ const styles$j = {
27932
27929
  answerBar: {
27933
27930
  display: "flex",
27934
27931
  alignItems: "center",
@@ -28121,7 +28118,7 @@ class GradedGroup extends React__namespace.Component {
28121
28118
  }));
28122
28119
  gradeStatus = this.context.strings.incorrect;
28123
28120
  }
28124
- const mobileClass = this.props.inGradedGroupSet ? aphrodite.css(styles$j.gradedGroupInSet) : aphrodite.css(styles$j.gradedGroup);
28121
+ const mobileClass = this.props.inGradedGroupSet ? aphrodite.css(styles$i.gradedGroupInSet) : aphrodite.css(styles$i.gradedGroup);
28125
28122
  const classes = classNames__default["default"]({
28126
28123
  [mobileClass]: apiOptions.isMobile,
28127
28124
  "perseus-graded-group": true,
@@ -28140,7 +28137,7 @@ class GradedGroup extends React__namespace.Component {
28140
28137
  return /*#__PURE__*/React__namespace.createElement("div", {
28141
28138
  className: classes
28142
28139
  }, !!this.props.title && /*#__PURE__*/React__namespace.createElement("div", {
28143
- className: aphrodite.css(styles$j.title)
28140
+ className: aphrodite.css(styles$i.title)
28144
28141
  }, this.props.title), /*#__PURE__*/React__namespace.createElement(Renderer, _extends({}, this.props, {
28145
28142
  ref: this.rendererRef,
28146
28143
  apiOptions: {
@@ -28173,7 +28170,7 @@ class GradedGroup extends React__namespace.Component {
28173
28170
  }, this.context.strings.nextQuestion), this.props.hint && this.props.hint.content && (this.state.showHint ? /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("button", {
28174
28171
  // @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
28175
28172
  tabIndex: "0",
28176
- className: aphrodite.css(styles$j.explanationTitle),
28173
+ className: aphrodite.css(styles$i.explanationTitle),
28177
28174
  onClick: () => this.setState({
28178
28175
  showHint: false
28179
28176
  }),
@@ -28205,7 +28202,7 @@ class GradedGroup extends React__namespace.Component {
28205
28202
  showHint: true
28206
28203
  });
28207
28204
  },
28208
- className: aphrodite.css(styles$j.showHintLink)
28205
+ className: aphrodite.css(styles$i.showHintLink)
28209
28206
  }, this.context.strings.explain)), apiOptions.isMobile && answerBarState !== "HIDDEN" && /*#__PURE__*/React__namespace.createElement(GradedGroupAnswerBar, {
28210
28207
  apiOptions: apiOptions,
28211
28208
  answerBarState: answerBarState,
@@ -28217,7 +28214,7 @@ class GradedGroup extends React__namespace.Component {
28217
28214
  const traverseChildWidgets$2 = function (props, traverseRenderer) {
28218
28215
  return ___default["default"].extend({}, props, traverseRenderer(props));
28219
28216
  };
28220
- const styles$j = aphrodite.StyleSheet.create({
28217
+ const styles$i = aphrodite.StyleSheet.create({
28221
28218
  gradedGroupInSet: {
28222
28219
  // Reset a few desktop-only styles that come from graded-group.less
28223
28220
  marginLeft: 0,
@@ -28298,20 +28295,20 @@ class Indicators extends React__namespace.Component {
28298
28295
  return /*#__PURE__*/React__namespace.createElement("ul", {
28299
28296
  // reduntantly add class name for use in .less files--
28300
28297
  // the styles object key gets hashed
28301
- className: classNames__default["default"](aphrodite.css(styles$i.indicatorContainer), "indicatorContainer")
28298
+ className: classNames__default["default"](aphrodite.css(styles$h.indicatorContainer), "indicatorContainer")
28302
28299
  }, this.props.gradedGroups.map((_ref, i) => {
28303
28300
  let {
28304
28301
  title
28305
28302
  } = _ref;
28306
28303
  return /*#__PURE__*/React__namespace.createElement("li", {
28307
- className: aphrodite.css(styles$i.indicator),
28304
+ className: aphrodite.css(styles$h.indicator),
28308
28305
  key: title
28309
28306
  }, /*#__PURE__*/React__namespace.createElement(Clickable__default["default"], {
28310
28307
  role: "button",
28311
28308
  "aria-label": this.context.strings.skipToTitle({
28312
28309
  title
28313
28310
  }),
28314
- style: styles$i.indicatorButton,
28311
+ style: styles$h.indicatorButton,
28315
28312
  onClick: () => this.props.onChangeCurrentGroup(i),
28316
28313
  onKeyDown: e => this.handleKeyDown(e, i)
28317
28314
  }, _ref2 => {
@@ -28321,9 +28318,9 @@ class Indicators extends React__namespace.Component {
28321
28318
  pressed
28322
28319
  } = _ref2;
28323
28320
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
28324
- style: [styles$i.indicatorDot, (hovered || focused || pressed) && styles$i.indicatorDotFocused]
28321
+ style: [styles$h.indicatorDot, (hovered || focused || pressed) && styles$h.indicatorDotFocused]
28325
28322
  }, i === this.props.currentGroup && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
28326
- style: styles$i.indicatorDotActive
28323
+ style: styles$h.indicatorDotActive
28327
28324
  }, /*#__PURE__*/React__namespace.createElement("span", {
28328
28325
  className: aphrodite.css(a11y.srOnly)
28329
28326
  }, this.context.strings.current)));
@@ -28403,7 +28400,7 @@ class GradedGroupSet extends React__namespace.Component {
28403
28400
  } = getDependencies();
28404
28401
  if (JIPT.useJIPT && this.props.gradedGroups.length > 1) {
28405
28402
  return /*#__PURE__*/React__namespace.createElement("div", {
28406
- className: aphrodite.css(styles$i.container)
28403
+ className: aphrodite.css(styles$h.container)
28407
28404
  }, this.props.gradedGroups.map((gradedGroup, i) => {
28408
28405
  return (
28409
28406
  /*#__PURE__*/
@@ -28426,13 +28423,13 @@ class GradedGroupSet extends React__namespace.Component {
28426
28423
  const numGroups = this.props.gradedGroups.length;
28427
28424
  const handleNextQuestion = this.state.currentGroup < numGroups - 1 ? this.handleNextQuestion : null;
28428
28425
  return /*#__PURE__*/React__namespace.createElement("div", {
28429
- className: aphrodite.css(styles$i.container)
28426
+ className: aphrodite.css(styles$h.container)
28430
28427
  }, /*#__PURE__*/React__namespace.createElement("div", {
28431
- className: aphrodite.css(styles$i.top)
28428
+ className: aphrodite.css(styles$h.top)
28432
28429
  }, /*#__PURE__*/React__namespace.createElement("div", {
28433
- className: aphrodite.css(styles$i.title)
28430
+ className: aphrodite.css(styles$h.title)
28434
28431
  }, currentGroup.title), /*#__PURE__*/React__namespace.createElement("div", {
28435
- className: aphrodite.css(styles$i.spacer)
28432
+ className: aphrodite.css(styles$h.spacer)
28436
28433
  }), /*#__PURE__*/React__namespace.createElement(Indicators, {
28437
28434
  currentGroup: this.state.currentGroup,
28438
28435
  gradedGroups: this.props.gradedGroups,
@@ -28470,7 +28467,7 @@ var GradedGroupSet$1 = {
28470
28467
  tracking: "all",
28471
28468
  isLintable: true
28472
28469
  };
28473
- const styles$i = aphrodite.StyleSheet.create({
28470
+ const styles$h = aphrodite.StyleSheet.create({
28474
28471
  top: {
28475
28472
  display: "flex",
28476
28473
  flexDirection: "row"
@@ -28576,7 +28573,7 @@ class ButtonGroup extends React__namespace.Component {
28576
28573
  id: "" + i,
28577
28574
  ref: "button" + i,
28578
28575
  key: "" + i,
28579
- className: aphrodite.css(styles$h.buttonStyle, button.value === value && styles$h.selectedStyle),
28576
+ className: aphrodite.css(styles$g.buttonStyle, button.value === value && styles$g.selectedStyle),
28580
28577
  onClick: () => this.toggleSelect(button.value)
28581
28578
  }, button.content || "" + button.value);
28582
28579
  });
@@ -28589,7 +28586,7 @@ class ButtonGroup extends React__namespace.Component {
28589
28586
  }, buttons);
28590
28587
  }
28591
28588
  }
28592
- const styles$h = aphrodite.StyleSheet.create({
28589
+ const styles$g = aphrodite.StyleSheet.create({
28593
28590
  buttonStyle: {
28594
28591
  backgroundColor: "white",
28595
28592
  border: "1px solid #ccc",
@@ -31967,7 +31964,7 @@ const VisibleIcon = () => /*#__PURE__*/React__namespace.createElement("svg", {
31967
31964
  width: "24",
31968
31965
  height: "24",
31969
31966
  viewBox: "0 0 24 24",
31970
- className: aphrodite.css(styles$g.icon)
31967
+ className: aphrodite.css(styles$f.icon)
31971
31968
  }, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("path", {
31972
31969
  id: "a",
31973
31970
  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"
@@ -31998,7 +31995,7 @@ const HiddenIcon = () => /*#__PURE__*/React__namespace.createElement("svg", {
31998
31995
  width: "24",
31999
31996
  height: "24",
32000
31997
  viewBox: "0 0 24 24",
32001
- className: aphrodite.css(styles$g.icon)
31998
+ className: aphrodite.css(styles$f.icon)
32002
31999
  }, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("path", {
32003
32000
  id: "a",
32004
32001
  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"
@@ -32037,20 +32034,20 @@ const HUD = _ref => {
32037
32034
  let state;
32038
32035
  let icon;
32039
32036
  if (enabled) {
32040
- state = styles$g.enabled;
32037
+ state = styles$f.enabled;
32041
32038
  icon = /*#__PURE__*/React__namespace.createElement(VisibleIcon, null);
32042
32039
  } else {
32043
- state = styles$g.disabled;
32040
+ state = styles$f.disabled;
32044
32041
  icon = /*#__PURE__*/React__namespace.createElement(HiddenIcon, null);
32045
32042
  }
32046
32043
  return /*#__PURE__*/React__namespace.createElement("button", {
32047
- className: aphrodite.css(styles$g.hud, fixedPosition && styles$g.hudFixedPosition, state),
32044
+ className: aphrodite.css(styles$f.hud, fixedPosition && styles$f.hudFixedPosition, state),
32048
32045
  onClick: e => {
32049
32046
  onClick();
32050
32047
  }
32051
32048
  }, icon, message);
32052
32049
  };
32053
- const styles$g = aphrodite.StyleSheet.create({
32050
+ const styles$f = aphrodite.StyleSheet.create({
32054
32051
  hud: {
32055
32052
  boxSizing: "border-box",
32056
32053
  height: 36,
@@ -32103,16 +32100,16 @@ const styles$g = aphrodite.StyleSheet.create({
32103
32100
  const InfoTipBase = props => {
32104
32101
  return /*#__PURE__*/React__namespace.createElement(Tooltip__default["default"], {
32105
32102
  content: /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.Body, {
32106
- style: styles$f.body
32103
+ style: styles$e.body
32107
32104
  }, props.children),
32108
32105
  placement: "right"
32109
32106
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksIcon.PhosphorIcon, {
32110
32107
  size: "small",
32111
32108
  icon: questionIcon__default["default"],
32112
- style: styles$f.spacingLeft
32109
+ style: styles$e.spacingLeft
32113
32110
  }));
32114
32111
  };
32115
- const styles$f = aphrodite.StyleSheet.create({
32112
+ const styles$e = aphrodite.StyleSheet.create({
32116
32113
  spacingLeft: {
32117
32114
  marginInlineStart: wonderBlocksTokens.spacing.xxxSmall_4
32118
32115
  },
@@ -32188,7 +32185,7 @@ class MultiButtonGroup extends React__namespace.Component {
32188
32185
  id: "" + i,
32189
32186
  key: "" + i,
32190
32187
  ref: "button" + i,
32191
- className: aphrodite.css(styles$e.buttonStyle, selected && styles$e.selectedStyle),
32188
+ className: aphrodite.css(styles$d.buttonStyle, selected && styles$d.selectedStyle),
32192
32189
  onClick: () => this.toggleSelect(button.value)
32193
32190
  }, button.content || "" + button.value);
32194
32191
  });
@@ -32201,7 +32198,7 @@ class MultiButtonGroup extends React__namespace.Component {
32201
32198
  }, buttons);
32202
32199
  }
32203
32200
  }
32204
- const styles$e = aphrodite.StyleSheet.create({
32201
+ const styles$d = aphrodite.StyleSheet.create({
32205
32202
  buttonStyle: {
32206
32203
  backgroundColor: "white",
32207
32204
  border: "1px solid #ccc",
@@ -32534,11 +32531,39 @@ const LegacyGrid = _ref => {
32534
32531
  return null;
32535
32532
  };
32536
32533
 
32534
+ /**
32535
+ * The Perseus "data schema" file.
32536
+ *
32537
+ * This file, and the types in it, represents the "data schema" that Perseus
32538
+ * uses. The @khanacademy/perseus-editor package edits and produces objects
32539
+ * that conform to the types in this file. Similarly, the top-level renderers
32540
+ * in @khanacademy/perseus, consume objects that conform to these types.
32541
+ *
32542
+ * WARNING: This file should not import any types from elsewhere so that it is
32543
+ * easy to reason about changes that alter the Perseus schema. This helps
32544
+ * ensure that it is not changed accidentally when upgrading a dependant
32545
+ * package or other part of Perseus code. Note that TypeScript does type
32546
+ * checking via something called "structural typing". This means that as long
32547
+ * as the shape of a type matches, the name it goes by doesn't matter. As a
32548
+ * result, a `Coord` type that looks like this `[x: number, y: number]` is
32549
+ * _identical_, in TypeScript's eyes, to this `Vector2` type `[x: number, y:
32550
+ * number]`. Also, with tuples, the labels for each entry is ignored, so `[x:
32551
+ * number, y: number]` is compatible with `[min: number, max: number]`. The
32552
+ * labels are for humans, not TypeScript. :)
32553
+ *
32554
+ * If you make changes to types in this file, be very sure that:
32555
+ *
32556
+ * a) the changes are backwards compatible. If they are not, old data from
32557
+ * previous versions of the "schema" could become unrenderable, or worse,
32558
+ * introduce hard-to-diagnose bugs.
32559
+ * b) the parsing code (`util/parse-perseus-json/`) is updated to handle
32560
+ * the new format _as well as_ the old format.
32561
+ */
32562
+
32537
32563
  // TODO(FEI-4010): Remove `Perseus` prefix for all types here
32538
- // TODO(FEI-4011): Use types generated by https://github.com/jaredly/generate-perseus-flowtypes
32539
32564
 
32540
- // Range is replaced within this file with Interval, but it is used elsewhere
32541
- // and exported from the package, so we need to keep it around.
32565
+ // Same name as Mafs
32566
+
32542
32567
  /**
32543
32568
  * Our core set of Perseus widgets.
32544
32569
  *
@@ -32582,6 +32607,7 @@ const LegacyGrid = _ref => {
32582
32607
  *
32583
32608
  * @see {@link https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation}
32584
32609
  */
32610
+
32585
32611
  /**
32586
32612
  * A map of widget IDs to widget options. This is most often used as the type
32587
32613
  * for a set of widgets defined in a `PerseusItem` but can also be useful to
@@ -32594,6 +32620,7 @@ const LegacyGrid = _ref => {
32594
32620
  * @see {@link PerseusWidgetTypes} additional widgets can be added to this map type
32595
32621
  * by augmenting the PerseusWidgetTypes with new widget types!
32596
32622
  */
32623
+
32597
32624
  /**
32598
32625
  * A "PerseusItem" is a classic Perseus item. It is rendered by the
32599
32626
  * `ServerItemRenderer` and the layout is pre-set.
@@ -32601,18 +32628,12 @@ const LegacyGrid = _ref => {
32601
32628
  * To render more complex Perseus items, see the `Item` type in the multi item
32602
32629
  * area.
32603
32630
  */
32631
+
32604
32632
  /**
32605
32633
  * A "PerseusArticle" is an item that is meant to be rendered as an article.
32606
32634
  * This item is never scored and is rendered by the `ArticleRenderer`.
32607
32635
  */
32608
- /**
32609
- * A "MultiItem" is an advanced Perseus item. It is rendered by the
32610
- * `MultiRenderer` and you can control the layout of individual parts of the
32611
- * item.
32612
- *
32613
- * @deprecated MultiItem support is slated for removal in a future Perseus
32614
- * release.
32615
- */
32636
+
32616
32637
  const ItemExtras = [
32617
32638
  // The user might benefit from using a Scientific Calculator. Provided on Khan Academy when true
32618
32639
  "calculator",
@@ -39071,7 +39092,7 @@ const HideAnswersToggle = props => {
39071
39092
  strings
39072
39093
  } = usePerseusI18n();
39073
39094
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
39074
- style: styles$d.switchWrapper
39095
+ style: styles$c.switchWrapper
39075
39096
  }, /*#__PURE__*/React__namespace.createElement(Switch__default["default"], {
39076
39097
  id: switchId,
39077
39098
  checked: props.areAnswersHidden,
@@ -39083,7 +39104,7 @@ const HideAnswersToggle = props => {
39083
39104
  tag: "label"
39084
39105
  }, strings.hideAnswersToggleLabel));
39085
39106
  };
39086
- const styles$d = aphrodite.StyleSheet.create({
39107
+ const styles$c = aphrodite.StyleSheet.create({
39087
39108
  switchWrapper: {
39088
39109
  display: "flex",
39089
39110
  flexDirection: "row",
@@ -39141,7 +39162,7 @@ const AnswerPill = props => {
39141
39162
  id: idFactory.get("perseus-label-image-widget-answer-pill"),
39142
39163
  onClick: correct ? undefined : onClick,
39143
39164
  ref: ref,
39144
- style: [style, popperStyle, styles$c.pill, correct && styles$c.correct, incorrect && styles$c.incorrect, (focused || hovered) && BringToFront]
39165
+ style: [style, popperStyle, styles$b.pill, correct && styles$b.correct, incorrect && styles$b.incorrect, (focused || hovered) && BringToFront]
39145
39166
  }, /*#__PURE__*/React__namespace.createElement(Renderer, {
39146
39167
  content: answerString,
39147
39168
  strings: strings,
@@ -39149,7 +39170,7 @@ const AnswerPill = props => {
39149
39170
  }));
39150
39171
  });
39151
39172
  };
39152
- const styles$c = aphrodite.StyleSheet.create({
39173
+ const styles$b = aphrodite.StyleSheet.create({
39153
39174
  correct: {
39154
39175
  // WB green darkened by 18%
39155
39176
  backgroundColor: "#00880b"
@@ -39215,25 +39236,25 @@ class Marker extends React__namespace.Component {
39215
39236
  icon: iconNull
39216
39237
  };
39217
39238
  if (showCorrectness) {
39218
- iconStyles = [styles$b.markerGraded, showCorrectness === "correct" ? styles$b.markerCorrect : styles$b.markerIncorrect, isOpen && styles$b.markerSelected];
39239
+ iconStyles = [styles$a.markerGraded, showCorrectness === "correct" ? styles$a.markerCorrect : styles$a.markerIncorrect, isOpen && styles$a.markerSelected];
39219
39240
  args = {
39220
39241
  ...args,
39221
39242
  icon: showCorrectness === "correct" ? iconCheck : iconMinus
39222
39243
  };
39223
39244
  } else if (selectedAnswers && selectedAnswers.length > 0) {
39224
- iconStyles = [styles$b.markerFilled, isOpen && styles$b.markerSelected];
39245
+ iconStyles = [styles$a.markerFilled, isOpen && styles$a.markerSelected];
39225
39246
  } else if (isOpen) {
39226
- iconStyles = [styles$b.markerSelected];
39247
+ iconStyles = [styles$a.markerSelected];
39227
39248
  args = {
39228
39249
  ...args,
39229
39250
  icon: iconChevronDown,
39230
39251
  size: 8
39231
39252
  };
39232
39253
  } else if (showPulsate) {
39233
- iconStyles = [styles$b.markerPulsateBase, this._mounted && shouldReduceMotion() ? showPulsate && styles$b.markerUnfilledPulsateOnce : showPulsate && styles$b.markerUnfilledPulsateInfinite];
39254
+ iconStyles = [styles$a.markerPulsateBase, this._mounted && shouldReduceMotion() ? showPulsate && styles$a.markerUnfilledPulsateOnce : showPulsate && styles$a.markerUnfilledPulsateInfinite];
39234
39255
  }
39235
39256
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
39236
- style: [styles$b.markerIcon, iconStyles],
39257
+ style: [styles$a.markerIcon, iconStyles],
39237
39258
  ref: node => this._icon = node
39238
39259
  }, /*#__PURE__*/React__namespace.createElement(Icon, args));
39239
39260
  }
@@ -39254,7 +39275,7 @@ class Marker extends React__namespace.Component {
39254
39275
  const markerDisabled = showCorrectness === "correct";
39255
39276
  const active = hovered || focused;
39256
39277
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
39257
- style: [styles$b.marker, active && !markerDisabled && styles$b.markerActive],
39278
+ style: [styles$a.marker, active && !markerDisabled && styles$a.markerActive],
39258
39279
  "aria-label": markerDisabled ? this.context.strings.correctExcited : label
39259
39280
  }, this.renderIcon()), !!selected && showAnswer && /*#__PURE__*/React__namespace.createElement(AnswerPill, {
39260
39281
  selectedAnswers: selected,
@@ -39267,7 +39288,7 @@ class Marker extends React__namespace.Component {
39267
39288
  }));
39268
39289
  }
39269
39290
  }
39270
- const styles$b = aphrodite.StyleSheet.create({
39291
+ const styles$a = aphrodite.StyleSheet.create({
39271
39292
  marker: {
39272
39293
  position: "absolute",
39273
39294
  backgroundColor: wonderBlocksTokens.color.white,
@@ -39861,13 +39882,13 @@ class LabelImage extends React__namespace.Component {
39861
39882
  const promptString = isMobile ? multipleAnswers ? strings.tapMultiple : strings.tapSingle : multipleAnswers ? strings.clickMultiple : strings.clickSingle;
39862
39883
  const choicesString = strings.choices;
39863
39884
  return /*#__PURE__*/React__namespace.createElement("div", {
39864
- className: classNames__default["default"]("perseus-label-image-widget-instructions", aphrodite.css(styles$a.instructions))
39885
+ className: classNames__default["default"]("perseus-label-image-widget-instructions", aphrodite.css(styles$9.instructions))
39865
39886
  }, /*#__PURE__*/React__namespace.createElement("div", {
39866
- className: aphrodite.css(styles$a.instructionsCaption)
39887
+ className: aphrodite.css(styles$9.instructionsCaption)
39867
39888
  }, promptString, " ", !hideChoices && choicesString), !hideChoices && /*#__PURE__*/React__namespace.createElement("div", {
39868
- className: aphrodite.css(styles$a.instructionsChoices)
39889
+ className: aphrodite.css(styles$9.instructionsChoices)
39869
39890
  }, choices.map((choice, index) => /*#__PURE__*/React__namespace.createElement("div", {
39870
- className: aphrodite.css(styles$a.instructionsChoice),
39891
+ className: aphrodite.css(styles$9.instructionsChoice),
39871
39892
  key: index
39872
39893
  }, /*#__PURE__*/React__namespace.createElement(Renderer, {
39873
39894
  content: choice,
@@ -39885,20 +39906,20 @@ class LabelImage extends React__namespace.Component {
39885
39906
  activeMarkerIndex
39886
39907
  } = this.state;
39887
39908
  return /*#__PURE__*/React__namespace.createElement("div", null, this.renderInstructions(), /*#__PURE__*/React__namespace.createElement("div", {
39888
- className: aphrodite.css(styles$a.markersCanvas),
39909
+ className: aphrodite.css(styles$9.markersCanvas),
39889
39910
  style: {
39890
39911
  maxWidth: imageWidth,
39891
39912
  maxHeight: imageHeight
39892
39913
  }
39893
39914
  }, /*#__PURE__*/React__namespace.createElement("div", {
39894
- className: aphrodite.css(styles$a.imageContainer,
39915
+ className: aphrodite.css(styles$9.imageContainer,
39895
39916
  // Ensure image interaction is disabled while answer
39896
39917
  // choices popup is visible. This addresses specific
39897
39918
  // case where user may click on question image to
39898
39919
  // dismiss the popup. If the image is larger in size
39899
39920
  // than its rendered in the widget, this would
39900
39921
  // result in a zoom of the image.
39901
- activeMarkerIndex !== -1 && styles$a.imageInteractionDisabled)
39922
+ activeMarkerIndex !== -1 && styles$9.imageInteractionDisabled)
39902
39923
  }, /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null, _ref3 => {
39903
39924
  let {
39904
39925
  setAssetStatus
@@ -39924,7 +39945,7 @@ class LabelImage extends React__namespace.Component {
39924
39945
  }));
39925
39946
  }
39926
39947
  }
39927
- const styles$a = aphrodite.StyleSheet.create({
39948
+ const styles$9 = aphrodite.StyleSheet.create({
39928
39949
  instructions: {
39929
39950
  paddingBottom: 16
39930
39951
  },
@@ -40012,7 +40033,7 @@ class Placeholder extends React__namespace.Component {
40012
40033
  const {
40013
40034
  layout
40014
40035
  } = this.props;
40015
- const className = aphrodite.css(styles$9.card, styles$9.placeholder, layout === "horizontal" && styles$9.horizontalCard);
40036
+ const className = aphrodite.css(styles$8.card, styles$8.placeholder, layout === "horizontal" && styles$8.horizontalCard);
40016
40037
  const style = {
40017
40038
  width: this.props.width,
40018
40039
  height: this.props.height
@@ -40219,7 +40240,7 @@ class Draggable extends React__namespace.Component {
40219
40240
  // We need to keep backwards compatbility with rules specified directly
40220
40241
  // in CSS. Hence the hacky tacking on of manual classNames.
40221
40242
  // See sortable.less for details.
40222
- let className = aphrodite.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";
40243
+ let className = aphrodite.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";
40223
40244
  if (!includePadding) {
40224
40245
  className += " perseus-sortable-draggable-unpadded";
40225
40246
  }
@@ -40557,7 +40578,7 @@ class Sortable extends React__namespace.Component {
40557
40578
  } = this.props;
40558
40579
  // We need to keep backwards compatbility with rules specified directly
40559
40580
  // in CSS. See sortable.less for details.
40560
- const className = aphrodite.css(styles$9.sortable) + " perseus-sortable";
40581
+ const className = aphrodite.css(styles$8.sortable) + " perseus-sortable";
40561
40582
  const syncWidth = this.props.constraints?.width || layout === "vertical";
40562
40583
  const syncHeight = this.props.constraints?.height || layout === "horizontal";
40563
40584
  ___default["default"].each(this.state.items, function (item, i, items) {
@@ -40643,7 +40664,7 @@ class Sortable extends React__namespace.Component {
40643
40664
  }, cards);
40644
40665
  }
40645
40666
  }
40646
- const styles$9 = aphrodite.StyleSheet.create({
40667
+ const styles$8 = aphrodite.StyleSheet.create({
40647
40668
  sortable: {
40648
40669
  boxSizing: "border-box",
40649
40670
  float: "left",
@@ -40839,25 +40860,25 @@ class Matcher extends React__namespace.Component {
40839
40860
  };
40840
40861
  const cellMarginPx = this.props.apiOptions.isMobile ? 8 : 5;
40841
40862
  return /*#__PURE__*/React__namespace.createElement("table", {
40842
- className: aphrodite.css(styles$8.widget) + " " + HACKY_CSS_CLASSNAME
40863
+ className: aphrodite.css(styles$7.widget) + " " + HACKY_CSS_CLASSNAME
40843
40864
  }, /*#__PURE__*/React__namespace.createElement("tbody", null, showLabels && /*#__PURE__*/React__namespace.createElement("tr", {
40844
- className: aphrodite.css(styles$8.row)
40865
+ className: aphrodite.css(styles$7.row)
40845
40866
  }, /*#__PURE__*/React__namespace.createElement("th", {
40846
- className: aphrodite.css(styles$8.column, styles$8.columnLabel)
40867
+ className: aphrodite.css(styles$7.column, styles$7.columnLabel)
40847
40868
  }, /*#__PURE__*/React__namespace.createElement(Renderer, {
40848
40869
  content: this.props.labels[0] || "...",
40849
40870
  linterContext: this.props.linterContext,
40850
40871
  strings: this.context.strings
40851
40872
  })), /*#__PURE__*/React__namespace.createElement("th", {
40852
- className: aphrodite.css(styles$8.column, styles$8.columnRight, styles$8.columnLabel)
40873
+ className: aphrodite.css(styles$7.column, styles$7.columnRight, styles$7.columnLabel)
40853
40874
  }, /*#__PURE__*/React__namespace.createElement(Renderer, {
40854
40875
  content: this.props.labels[1] || "...",
40855
40876
  linterContext: this.props.linterContext,
40856
40877
  strings: this.context.strings
40857
40878
  }))), /*#__PURE__*/React__namespace.createElement("tr", {
40858
- className: aphrodite.css(styles$8.row)
40879
+ className: aphrodite.css(styles$7.row)
40859
40880
  }, /*#__PURE__*/React__namespace.createElement("td", {
40860
- className: aphrodite.css(styles$8.column)
40881
+ className: aphrodite.css(styles$7.column)
40861
40882
  }, /*#__PURE__*/React__namespace.createElement(Sortable, {
40862
40883
  options: left,
40863
40884
  layout: "vertical",
@@ -40872,7 +40893,7 @@ class Matcher extends React__namespace.Component {
40872
40893
  ,
40873
40894
  ref: "left"
40874
40895
  })), /*#__PURE__*/React__namespace.createElement("td", {
40875
- className: aphrodite.css(styles$8.column, styles$8.columnRight)
40896
+ className: aphrodite.css(styles$7.column, styles$7.columnRight)
40876
40897
  }, /*#__PURE__*/React__namespace.createElement(Sortable, {
40877
40898
  options: right,
40878
40899
  layout: "vertical",
@@ -40890,7 +40911,7 @@ class Matcher extends React__namespace.Component {
40890
40911
  }
40891
40912
  const padding = 5;
40892
40913
  const border = "1px solid #444";
40893
- const styles$8 = aphrodite.StyleSheet.create({
40914
+ const styles$7 = aphrodite.StyleSheet.create({
40894
40915
  widget: {
40895
40916
  paddingTop: padding,
40896
40917
  maxWidth: "100%",
@@ -41581,9 +41602,9 @@ function drawText(ctx, item) {
41581
41602
  if (item.value === null) {
41582
41603
  return;
41583
41604
  }
41584
- ctx.fillStyle = styles$7.fgColor;
41585
- ctx.font = styles$7.font;
41586
- ctx.fillText(item.value, item.pos[0] - styles$7.fontSizePx / 2 + 1, item.pos[1] + styles$7.fontSizePx / 2);
41605
+ ctx.fillStyle = styles$6.fgColor;
41606
+ ctx.font = styles$6.font;
41607
+ ctx.fillText(item.value, item.pos[0] - styles$6.fontSizePx / 2 + 1, item.pos[1] + styles$6.fontSizePx / 2);
41587
41608
  }
41588
41609
 
41589
41610
  /**
@@ -41604,16 +41625,16 @@ function drawText(ctx, item) {
41604
41625
  function drawDoubleLine(ctx, item) {
41605
41626
  // Outer line that forms both of the bond lines.
41606
41627
  let path = new Path2D();
41607
- ctx.lineWidth = 5 * styles$7.lineWidth;
41608
- ctx.strokeStyle = styles$7.fgColor;
41628
+ ctx.lineWidth = 5 * styles$6.lineWidth;
41629
+ ctx.strokeStyle = styles$6.fgColor;
41609
41630
  path.moveTo(item.startPos[0], item.startPos[1]);
41610
41631
  path.lineTo(item.endPos[0], item.endPos[1]);
41611
41632
  ctx.stroke(path);
41612
41633
 
41613
41634
  // Inner white line that separates the two bond lines.
41614
41635
  path = new Path2D();
41615
- ctx.lineWidth = 3 * styles$7.lineWidth;
41616
- ctx.strokeStyle = styles$7.bgColor;
41636
+ ctx.lineWidth = 3 * styles$6.lineWidth;
41637
+ ctx.strokeStyle = styles$6.bgColor;
41617
41638
  path.moveTo(item.startPos[0], item.startPos[1]);
41618
41639
  path.lineTo(item.endPos[0], item.endPos[1]);
41619
41640
  ctx.stroke(path);
@@ -41640,16 +41661,16 @@ function drawTripleLine(ctx, item) {
41640
41661
 
41641
41662
  // Outer line that will form the two outer bond lines.
41642
41663
  let path = new Path2D();
41643
- ctx.lineWidth = 7 * styles$7.lineWidth;
41644
- ctx.strokeStyle = styles$7.fgColor;
41664
+ ctx.lineWidth = 7 * styles$6.lineWidth;
41665
+ ctx.strokeStyle = styles$6.fgColor;
41645
41666
  path.moveTo(item.startPos[0], item.startPos[1]);
41646
41667
  path.lineTo(item.endPos[0], item.endPos[1]);
41647
41668
  ctx.stroke(path);
41648
41669
 
41649
41670
  // Middle white line that separates the bonds
41650
41671
  path = new Path2D();
41651
- ctx.lineWidth = 5 * styles$7.lineWidth;
41652
- ctx.strokeStyle = styles$7.bgColor;
41672
+ ctx.lineWidth = 5 * styles$6.lineWidth;
41673
+ ctx.strokeStyle = styles$6.bgColor;
41653
41674
  path.moveTo(item.startPos[0], item.startPos[1]);
41654
41675
  path.lineTo(item.endPos[0], item.endPos[1]);
41655
41676
  ctx.stroke(path);
@@ -41671,8 +41692,8 @@ function drawTripleLine(ctx, item) {
41671
41692
  */
41672
41693
  function drawLine(ctx, item) {
41673
41694
  const path = new Path2D();
41674
- ctx.lineWidth = styles$7.lineWidth;
41675
- ctx.strokeStyle = styles$7.fgColor;
41695
+ ctx.lineWidth = styles$6.lineWidth;
41696
+ ctx.strokeStyle = styles$6.fgColor;
41676
41697
  path.moveTo(item.startPos[0], item.startPos[1]);
41677
41698
  path.lineTo(item.endPos[0], item.endPos[1]);
41678
41699
  ctx.stroke(path);
@@ -41724,7 +41745,7 @@ function compareElements(item0, item1) {
41724
41745
  function draw(ctx, items) {
41725
41746
  items.sort(compareElements).forEach(drawItem(ctx));
41726
41747
  }
41727
- const styles$7 = {
41748
+ const styles$6 = {
41728
41749
  bgColor: "rgb(255, 255, 255)",
41729
41750
  fgColor: "rgb(0, 0, 0)",
41730
41751
  fontSizePx: 12,
@@ -43851,7 +43872,7 @@ class PhetSimulation extends React__namespace.Component {
43851
43872
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
43852
43873
  const sandboxProperties = "allow-same-origin allow-scripts";
43853
43874
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
43854
- style: styles$6.widgetContainer
43875
+ style: styles$5.widgetContainer
43855
43876
  }, this.state.banner !== null &&
43856
43877
  /*#__PURE__*/
43857
43878
  // TODO(anna): Make this banner focusable
@@ -43864,12 +43885,12 @@ class PhetSimulation extends React__namespace.Component {
43864
43885
  kind: this.state.banner.kind,
43865
43886
  text: this.state.banner.message
43866
43887
  })), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
43867
- style: styles$6.iframeContainer
43888
+ style: styles$5.iframeContainer
43868
43889
  }, /*#__PURE__*/React__namespace.createElement("iframe", {
43869
43890
  ref: this.iframeRef,
43870
43891
  title: this.props.description,
43871
43892
  sandbox: sandboxProperties,
43872
- className: aphrodite.css(styles$6.iframeResponsive),
43893
+ className: aphrodite.css(styles$5.iframeResponsive),
43873
43894
  src: this.state.url?.toString(),
43874
43895
  allow: "fullscreen"
43875
43896
  })), this.state.url !== null && /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
@@ -43900,7 +43921,7 @@ const makeSafeUrl = (urlString, locale) => {
43900
43921
  url.searchParams.set("locale", locale);
43901
43922
  return url;
43902
43923
  };
43903
- const styles$6 = aphrodite.StyleSheet.create({
43924
+ const styles$5 = aphrodite.StyleSheet.create({
43904
43925
  widgetContainer: {
43905
43926
  borderRadius: 6,
43906
43927
  borderWidth: 1,
@@ -44823,7 +44844,7 @@ class PythonProgram extends React__namespace.Component {
44823
44844
  // creator "went wild".
44824
44845
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
44825
44846
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
44826
- style: styles$5.container
44847
+ style: styles$4.container
44827
44848
  }, /*#__PURE__*/React__namespace.createElement("iframe", {
44828
44849
  sandbox: sandboxOptions,
44829
44850
  src: url,
@@ -44832,7 +44853,7 @@ class PythonProgram extends React__namespace.Component {
44832
44853
  }));
44833
44854
  }
44834
44855
  }
44835
- const styles$5 = aphrodite.StyleSheet.create({
44856
+ const styles$4 = aphrodite.StyleSheet.create({
44836
44857
  container: {
44837
44858
  margin: "auto",
44838
44859
  width: "100%"
@@ -45318,7 +45339,7 @@ const VideoTranscriptLink = props => {
45318
45339
  {
45319
45340
  const video = result.data?.video;
45320
45341
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
45321
- style: styles$4.transcriptLink
45342
+ style: styles$3.transcriptLink
45322
45343
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.Text, null, video?.title), /*#__PURE__*/React__namespace.createElement(wonderBlocksLayout.Strut, {
45323
45344
  size: 10
45324
45345
  }), /*#__PURE__*/React__namespace.createElement(Link__default["default"], {
@@ -45335,7 +45356,7 @@ const VideoTranscriptLink = props => {
45335
45356
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, strings.somethingWrong);
45336
45357
  }
45337
45358
  };
45338
- const styles$4 = aphrodite.StyleSheet.create({
45359
+ const styles$3 = aphrodite.StyleSheet.create({
45339
45360
  transcriptLink: {
45340
45361
  flexDirection: "row",
45341
45362
  width: "100%",
@@ -45456,7 +45477,7 @@ const init = function () {
45456
45477
 
45457
45478
  // This file is processed by a Rollup plugin (replace) to inject the production
45458
45479
  const libName = "@khanacademy/perseus";
45459
- const libVersion = "46.0.1";
45480
+ const libVersion = "47.0.1";
45460
45481
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45461
45482
 
45462
45483
  const apiVersion = {
@@ -45673,7 +45694,7 @@ class HintRenderer extends React__namespace.Component {
45673
45694
  const {
45674
45695
  isMobile
45675
45696
  } = apiOptions;
45676
- const classNames = classNames__default["default"]("hint", !isMobile && "perseus-hint-renderer", isMobile && aphrodite.css(styles$3.newHint), isMobile && lastRendered && aphrodite.css(styles$3.lastRenderedNewHint), lastHint && "last-hint", lastRendered && "last-rendered", className);
45697
+ const classNames = classNames__default["default"]("hint", !isMobile && "perseus-hint-renderer", isMobile && aphrodite.css(styles$2.newHint), isMobile && lastRendered && aphrodite.css(styles$2.lastRenderedNewHint), lastHint && "last-hint", lastRendered && "last-rendered", className);
45677
45698
 
45678
45699
  // TODO(charlie): Allowing `customKeypad` would require that we
45679
45700
  // extend `ItemRenderer` to support nested inputs in the `HintsRenderer`.
@@ -45715,7 +45736,7 @@ class HintRenderer extends React__namespace.Component {
45715
45736
  );
45716
45737
  }
45717
45738
  }
45718
- const styles$3 = aphrodite.StyleSheet.create({
45739
+ const styles$2 = aphrodite.StyleSheet.create({
45719
45740
  newHint: {
45720
45741
  marginBottom: 1.5 * baseUnitPx,
45721
45742
  borderLeftColor: gray97,
@@ -45892,14 +45913,14 @@ class HintsRenderer extends React__namespace.Component {
45892
45913
  } = apiOptions;
45893
45914
  const showGetAnotherHint = getAnotherHint && hintsVisible > 0 && hintsVisible < this.props.hints.length;
45894
45915
  const hintRatioCopy = `(${hintsVisible}/${this.props.hints.length})`;
45895
- const classNames = classNames__default["default"](this.props.className, apiOptions.isMobile && hintsVisible > 0 && aphrodite.css(styles$2.mobileHintStylesHintsRenderer));
45916
+ const classNames = classNames__default["default"](this.props.className, apiOptions.isMobile && hintsVisible > 0 && aphrodite.css(styles$1.mobileHintStylesHintsRenderer));
45896
45917
  return /*#__PURE__*/React__namespace.createElement("div", {
45897
45918
  className: classNames
45898
45919
  }, apiOptions.isMobile && hintsVisible > 0 && /*#__PURE__*/React__namespace.createElement("div", {
45899
- className: aphrodite.css(styles$2.mobileHintStylesHintTitle, sharedStyles.responsiveLabel)
45920
+ className: aphrodite.css(styles$1.mobileHintStylesHintTitle, sharedStyles.responsiveLabel)
45900
45921
  }, this.context.strings.hints), hints, showGetAnotherHint && /*#__PURE__*/React__namespace.createElement("button", {
45901
45922
  rel: "button",
45902
- className: aphrodite.css(styles$2.linkButton, styles$2.getAnotherHintButton, apiOptions.isMobile && styles$2.mobileHintStylesGetAnotherHintButton),
45923
+ className: aphrodite.css(styles$1.linkButton, styles$1.getAnotherHintButton, apiOptions.isMobile && styles$1.mobileHintStylesGetAnotherHintButton),
45903
45924
  onClick: evt => {
45904
45925
  evt.preventDefault();
45905
45926
  evt.stopPropagation();
@@ -45908,14 +45929,14 @@ class HintsRenderer extends React__namespace.Component {
45908
45929
  }
45909
45930
  }
45910
45931
  }, /*#__PURE__*/React__namespace.createElement("span", {
45911
- className: aphrodite.css(styles$2.plusText, apiOptions.isMobile && styles$2.mobileHintStylesPlusText)
45932
+ className: aphrodite.css(styles$1.plusText, apiOptions.isMobile && styles$1.mobileHintStylesPlusText)
45912
45933
  }, "+"), /*#__PURE__*/React__namespace.createElement("span", {
45913
- className: aphrodite.css(styles$2.getAnotherHintText)
45934
+ className: aphrodite.css(styles$1.getAnotherHintText)
45914
45935
  }, this.context.strings.getAnotherHint, " ", hintRatioCopy)));
45915
45936
  }
45916
45937
  }
45917
45938
  const hintIndentation = baseUnitPx + hintBorderWidth;
45918
- const styles$2 = aphrodite.StyleSheet.create({
45939
+ const styles$1 = aphrodite.StyleSheet.create({
45919
45940
  // eslint-disable-next-line react-native/no-unused-styles
45920
45941
  rendererMargins: {
45921
45942
  marginTop: baseUnitPx
@@ -46335,11 +46356,11 @@ class ServerItemRenderer extends React__namespace.Component {
46335
46356
  // mobile hint styles, which are flush to the left.
46336
46357
  // NOTE(charlie): We may still want to apply this
46337
46358
  // padding for desktop exercises.
46338
- apiOptions.isMobile ? undefined : aphrodite.css(styles$1.hintsContainer)
46359
+ apiOptions.isMobile ? undefined : aphrodite.css(styles.hintsContainer)
46339
46360
  }, hintsRenderer)));
46340
46361
  }
46341
46362
  }
46342
- const styles$1 = aphrodite.StyleSheet.create({
46363
+ const styles = aphrodite.StyleSheet.create({
46343
46364
  hintsContainer: {
46344
46365
  marginLeft: 50
46345
46366
  }
@@ -46446,1148 +46467,6 @@ const traverse = function (rendererOptions, contentCallback, widgetCallback, opt
46446
46467
  return traverseRenderer$1(rendererOptions, contentCallback, deepCallbackFor(contentCallback, widgetCallback, optionsCallback), optionsCallback);
46447
46468
  };
46448
46469
 
46449
- /**
46450
- * Utility functions for constructing and inferring multi-item shapes.
46451
- *
46452
- * A shape is an object that serves as a runtime type declaration: it specifies
46453
- * a tree structure for a particular class of multi-item. See shape-types.js
46454
- * for further discussion.
46455
- *
46456
- * This module allows you to construct arbitrary Shape trees, by combining
46457
- * leaf node shapes like `content` and `hint` into composite shapes like
46458
- * `arrayOf(shape({foo: content, bar: hint}))`.
46459
- */
46460
-
46461
- /**
46462
- * These tools allow you to construct arbirtary shapes, by combining simple
46463
- * leaf shapes like `content` and `hint` into composite shapes like
46464
- * `arrayOf(shape({question: content, hints: arrayOf(hint)}))`.
46465
- */
46466
- const contentShape = {
46467
- type: "content"
46468
- };
46469
- const hintShape = {
46470
- type: "hint"
46471
- };
46472
- const tagsShape = {
46473
- type: "tags"
46474
- };
46475
- const buildArrayShape = elementShape => ({
46476
- type: "array",
46477
- elementShape
46478
- });
46479
- const buildObjectShape = shape => ({
46480
- type: "object",
46481
- shape
46482
- });
46483
- const hintsShape = buildArrayShape(hintShape);
46484
- var shapes = {
46485
- content: contentShape,
46486
- hint: hintShape,
46487
- hints: hintsShape,
46488
- tags: tagsShape,
46489
- arrayOf: buildArrayShape,
46490
- shape: buildObjectShape
46491
- };
46492
-
46493
- /**
46494
- * Utility functions for manipulating multi-item-shaped trees.
46495
- *
46496
- * Multi-items are trees! But we also often have other trees that are shaped
46497
- * like multi-items - for example, if we map a multi-item tree into a tree of
46498
- * renderer info and state, and then map that again into a tree of just the
46499
- * renderer nodes, like we do in MultiRenderer. See tree-types.js for further
46500
- * discussion.
46501
- *
46502
- * These functions enable us to manipulate generic multi-item-shaped trees,
46503
- * regardless of what type of data they contain at their leaves. You can use
46504
- * the mapper functions to transform a tree into another tree of the same
46505
- * shape, or to discover all the nodes of a particular type.
46506
- *
46507
- * We expose two simple mapper functions (mapContentNodes and mapHintNodes),
46508
- * and also a more complex interface for creating a mapping over all of a
46509
- * tree's node types simultaneously:
46510
- *
46511
- * `buildMapper()` returns a TreeMapper object that allows you to build your
46512
- * mapper object one node type at a time. Then, you can execute your mapping by
46513
- * calling the `mapTree` method.
46514
- *
46515
- * For example:
46516
- * const renderers = buildMapper()
46517
- * .setContentMapper(this.renderContentNode)
46518
- * .setHintMapper(this.renderHintNode)
46519
- * .setTagsMapper(this.renderTagsNode)
46520
- * .setArrayMapper(this.hideSkippedQuestions)
46521
- * .mapTree(tree, shape);
46522
- *
46523
- * This will copy the given tree, apply the given transformations to the
46524
- * content, hint, and array nodes respectively, and return the resulting tree.
46525
- *
46526
- * For node types whose mappers aren't specified, we default to the identity
46527
- * function. (This builder interface enables us to implement that default
46528
- * behavior in a provably type-safe way, while not requiring the call site to
46529
- * be aware of all the node types. Hooray!)
46530
- *
46531
- * The call to `setArrayMapper` must come last, because the array mapper's
46532
- * argument types depend on the other mappers' types. See ArrayMapper for more
46533
- * details.
46534
- *
46535
- * WARNING: These functions trust that the provided tree conforms to the
46536
- * provided shape. If not, behavior is undefined and may not respect the type
46537
- * signatures specified here.
46538
- */
46539
-
46540
- /**
46541
- * The sequence of edges that lead to a particular node in a Tree.
46542
- * Elements can be `string` to correspond to an ObjectNode key, or `number` to
46543
- * correspond to an ArrayNode index.
46544
- */
46545
-
46546
- /**
46547
- * These are function interfaces for mapping over various types of tree nodes.
46548
- *
46549
- * ArrayMapper is a bit more complicated than the leaf node mappers. It's
46550
- * executed in the context of a `mapTree` call, after we've finished mapping
46551
- * its child nodes, so the function has access to both the resulting array
46552
- * (with mapped elements) and the original array (with the original untouched
46553
- * elements).
46554
- *
46555
- * The ArrayMapper then has the opportunity to apply a final transformation to
46556
- * the resulting array, like filtering certain elements or (in the hacky
46557
- * MultiRenderer case) attaching a `renderHints` method to arrays of hint
46558
- * renderers :)
46559
- *
46560
- * This is why `TreeMapper#setArrayMapper` must be called last: ArrayMapper's
46561
- * types depend on the ContentMapper and HintMapper's types. And, since you can
46562
- * only specify one mapper at a time in this builder interface (which is
46563
- * necessary to provide default mappers in a type-safe way), you need your
46564
- * dependencies to already be in place by the time you call `setArrayMapper`.
46565
- * Otherwise, we'd have to set the ArrayMapper and *hope* that you *eventually*
46566
- * provide a compatible ContentMapper and HintMapper, which is difficult to
46567
- * prove at compile time.
46568
- *
46569
- * There's no ObjectMapper here, but not for any particular reason. We just
46570
- * don't have a use case for it yet, so we haven't built it yet.
46571
- */
46572
-
46573
- /**
46574
- * A TreeMapper is a collection of node mappers, which, together, compose the
46575
- * behavior for mapping over an entire tree.
46576
- *
46577
- * This serves as the interface for the two TreeMapper classes, including both
46578
- * the internal mapper properties that we care about, and the `mapTree`
46579
- * function that the call site will use.
46580
- */
46581
-
46582
- /**
46583
- * This is a TreeMapper that only has mappers specified for its leaf nodes; its
46584
- * array mapper is the identity function.
46585
- *
46586
- * This is the TreeMapper initially returned by `buildMapper`. It allows you to
46587
- * change the types of your ContentMapper and HintMapper, which is safe because
46588
- * none of the other mappers that depend on those types (aka ArrayMapper) have
46589
- * been specified yet. (Or, more specifically, the ArrayMapper is currently
46590
- * `identity`, which can trivially vary with the ContentMapper and HintMapper's
46591
- * types.)
46592
- *
46593
- * Once you call `setArrayMapper`, however, we move to the other class:
46594
- * TreeMapperForLeavesAndCollections.
46595
- */
46596
- class TreeMapperJustForLeaves {
46597
- content;
46598
- hint;
46599
- tags;
46600
- array;
46601
- constructor(content, hint, tags) {
46602
- this.content = content;
46603
- this.hint = hint;
46604
- this.tags = tags;
46605
- this.array = identity;
46606
- }
46607
- setContentMapper(newContentMapper) {
46608
- return new TreeMapperJustForLeaves(newContentMapper, this.hint, this.tags);
46609
- }
46610
- setHintMapper(newHintMapper) {
46611
- return new TreeMapperJustForLeaves(this.content, newHintMapper, this.tags);
46612
- }
46613
- setTagsMapper(newTagsMapper) {
46614
- return new TreeMapperJustForLeaves(this.content, this.hint, newTagsMapper);
46615
- }
46616
- setArrayMapper(newArrayMapper) {
46617
- return new TreeMapperForLeavesAndCollections(this.content, this.hint, this.tags, newArrayMapper);
46618
- }
46619
-
46620
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic. | TS2315 - Type 'Tree' is not generic.
46621
- mapTree(tree, shape) {
46622
- return mapTree(tree, shape, [], this);
46623
- }
46624
- }
46625
-
46626
- /**
46627
- * This is a TreeMapper that already has an ArrayMapper specified, so its
46628
- * ContentMapper and HintMapper are now locked in.
46629
- */
46630
- class TreeMapperForLeavesAndCollections {
46631
- content;
46632
- hint;
46633
- tags;
46634
- array;
46635
- constructor(content, hint, tags, array) {
46636
- this.content = content;
46637
- this.hint = hint;
46638
- this.tags = tags;
46639
- this.array = array;
46640
- }
46641
- setArrayMapper(newArrayMapper) {
46642
- return new TreeMapperForLeavesAndCollections(this.content, this.hint, this.tags, newArrayMapper);
46643
- }
46644
-
46645
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic. | TS2315 - Type 'Tree' is not generic.
46646
- mapTree(tree, shape) {
46647
- return mapTree(tree, shape, [], this);
46648
- }
46649
- }
46650
- function identity(x) {
46651
- return x;
46652
- }
46653
-
46654
- /**
46655
- * Return a new TreeMapper that will perform a no-op transformation on an input
46656
- * tree. To make it useful, chain any combination of `setContentMapper`,
46657
- * `setHintMapper`, `setTagMapper`, and `setArrayMapper` to specify
46658
- * transformations for the individual node types.
46659
- */
46660
- // @ts-expect-error - TS2300 - Duplicate identifier 'C'. | TS2300 - Duplicate identifier 'H'. | TS2300 - Duplicate identifier 'T'.
46661
- function buildMapper() {
46662
- return new TreeMapperJustForLeaves(identity, identity, identity);
46663
- }
46664
-
46665
- /**
46666
- * Copy the given tree, apply the corresponding transformation specified in the
46667
- * TreeMapper to each node, and return the resulting tree.
46668
- */
46669
- function mapTree(
46670
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46671
- tree, shape, path, mappers
46672
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46673
- ) {
46674
- // We trust the shape of the multi-item to match the shape provided at
46675
- // runtime. Therefore, in each shape branch, we cast the node to `any` and
46676
- // reinterpret it as the expected node type.
46677
- if (shape.type === "content") {
46678
- const content = tree;
46679
- return mappers.content(content, shape, path);
46680
- }
46681
- if (shape.type === "hint") {
46682
- const hint = tree;
46683
- return mappers.hint(hint, shape, path);
46684
- }
46685
- if (shape.type === "tags") {
46686
- const tags = tree;
46687
- return mappers.tags(tags, shape, path);
46688
- }
46689
- if (shape.type === "array") {
46690
- // @ts-expect-error - TS2315 - Type 'ArrayNode' is not generic.
46691
- const array = tree;
46692
- if (!Array.isArray(array)) {
46693
- throw new perseusCore.PerseusError(`Invalid object of type "${typeof array}" found at path ` +
46694
- // @ts-expect-error - TS2769 - No overload matches this call.
46695
- `${["<root>"].concat(path).join(".")}. Expected array.`, perseusCore.Errors.Internal);
46696
- }
46697
- const elementShape = shape.elementShape;
46698
- // @ts-expect-error - TS2315 - Type 'ArrayNode' is not generic.
46699
- const mappedElements = array.map((inner, i) => mapTree(inner, elementShape, path.concat(i), mappers));
46700
- return mappers.array(mappedElements, array, shape, path);
46701
- }
46702
- if (shape.type === "object") {
46703
- const object = tree;
46704
- if (object && typeof object !== "object") {
46705
- throw new perseusCore.PerseusError(`Invalid object of type "${typeof object}" found at ` +
46706
- // @ts-expect-error - TS2769 - No overload matches this call.
46707
- `path ${["<root>"].concat(path).join(".")}. Expected ` + `"object" type.`, perseusCore.Errors.InvalidInput);
46708
- }
46709
- const valueShapes = shape.shape;
46710
- if (!valueShapes) {
46711
- throw new perseusCore.PerseusError(`Unexpected shape ${JSON.stringify(shape)} at path ` +
46712
- // @ts-expect-error - TS2769 - No overload matches this call.
46713
- `${["<root>"].concat(path).join(".")}.`, perseusCore.Errors.InvalidInput);
46714
- }
46715
- const newObject = {};
46716
- Object.keys(valueShapes).forEach(key => {
46717
- if (!(key in object)) {
46718
- throw new perseusCore.PerseusError(`Key "${key}" is missing from shape at path ` +
46719
- // @ts-expect-error - TS2769 - No overload matches this call.
46720
- `${["<root>"].concat(path).join(".")}.`, perseusCore.Errors.InvalidInput);
46721
- }
46722
- newObject[key] = mapTree(object[key], valueShapes[key], path.concat(key), mappers);
46723
- });
46724
- return newObject;
46725
- }
46726
- throw new perseusCore.PerseusError(
46727
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
46728
- `unexpected shape type ${shape.type}`, perseusCore.Errors.InvalidInput);
46729
- }
46730
-
46731
- /**
46732
- * Utility functions for constructing and manipulating multi-items.
46733
- *
46734
- * These functions apply *specifically* to Items and ItemTrees - things that
46735
- * actually semantically *are* multi-items. For more general functions for
46736
- * traversing and manipulating *anything* shaped like a multi-item (like a
46737
- * renderer tree or a score tree or, well, a multi-item), see trees.js.
46738
- */
46739
- /**
46740
- * Return a semantically empty ItemTree that conforms to the given shape.
46741
- *
46742
- * - An empty content node has an empty content string and no widgets/images.
46743
- * - An empty hint node has an empty content string and no widgets/images.
46744
- * - An empty array node has no elements.
46745
- * - An empty object node has a semantically empty node for each of its keys.
46746
- * (That is, we recursively call buildEmptyItemTreeForShape for each key.)
46747
- */
46748
- function buildEmptyItemTreeForShape(shape) {
46749
- if (shape.type === "content") {
46750
- return {
46751
- __type: "content",
46752
- content: "",
46753
- images: {},
46754
- widgets: {}
46755
- };
46756
- }
46757
- if (shape.type === "hint") {
46758
- return {
46759
- __type: "hint",
46760
- replace: false,
46761
- content: "",
46762
- images: {},
46763
- widgets: {}
46764
- };
46765
- }
46766
- if (shape.type === "tags") {
46767
- return [];
46768
- }
46769
- if (shape.type === "array") {
46770
- return [];
46771
- }
46772
- if (shape.type === "object") {
46773
- const valueShapes = shape.shape;
46774
- const object = {};
46775
- Object.keys(valueShapes).forEach(key => {
46776
- object[key] = buildEmptyItemTreeForShape(valueShapes[key]);
46777
- });
46778
- return object;
46779
- }
46780
- throw new perseusCore.PerseusError(
46781
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
46782
- `unexpected shape type ${shape.type}`, perseusCore.Errors.InvalidInput);
46783
- }
46784
-
46785
- /**
46786
- * Return a semantically empty Item that conforms to the given shape.
46787
- *
46788
- * - An empty content node has an empty content string and no widgets/images.
46789
- * - An empty hint node has an empty content string and no widgets/images.
46790
- * - An empty array node has no elements.
46791
- * - An empty object node has a semantically empty node for each of its keys.
46792
- * (That is, we recursively call buildEmptyItemTreeForShape for each key.)
46793
- */
46794
- function buildEmptyItemForShape(shape) {
46795
- return treeToItem(buildEmptyItemTreeForShape(shape));
46796
- }
46797
-
46798
- /**
46799
- * Given an Item and its Shape, yield all of its content nodes to the callback.
46800
- */
46801
- function findContentNodesInItem$2(item, shape, callback) {
46802
- const itemTree = itemToTree(item);
46803
- buildMapper().setContentMapper(callback).mapTree(itemTree, shape);
46804
- }
46805
-
46806
- /**
46807
- * Given an Item and its Shape, yield all of its hint nodes to the callback.
46808
- */
46809
- function findHintNodesInItem(item, shape, callback) {
46810
- const itemTree = itemToTree(item);
46811
- buildMapper().setHintMapper(callback).mapTree(itemTree, shape);
46812
- }
46813
-
46814
- /**
46815
- * Given an ItemTree, return a Shape that it conforms to.
46816
- *
46817
- * The Shape might not be complete or correct Shape that this Item was designed
46818
- * for. If you have access to the intended Shape, use that instead.
46819
- */
46820
- function inferItemShape$2(item) {
46821
- const itemTree = itemToTree(item);
46822
- return inferItemTreeShape(itemTree);
46823
- }
46824
- function inferItemTreeShape(node) {
46825
- if (Array.isArray(node)) {
46826
- if (node.length) {
46827
- if (typeof node[0] === "string") {
46828
- // There's no ItemTree that can manifest as a string.
46829
- // So, an array of strings must be a TagsNode, not ArrayNode.
46830
- return shapes.tags;
46831
- }
46832
- // Otherwise, assume that this is a valid ArrayNode, and
46833
- // therefore the shape of the first element applies to all
46834
- // elements in the array.
46835
- return shapes.arrayOf(inferItemTreeShape(node[0]));
46836
- }
46837
- // The array is empty, so we arbitrarily guess that it's a content
46838
- // array. As discussed in the docstring, this might be incorrect,
46839
- // and you shouldn't depend on it.
46840
- return shapes.arrayOf(shapes.content);
46841
- }
46842
- if (
46843
- // TODO(mdr): Remove #LegacyContentNode support.
46844
- typeof node === "object" && (node.__type === "content" || node.__type === "item")) {
46845
- return shapes.content;
46846
- }
46847
- if (typeof node === "object" && node.__type === "hint") {
46848
- return shapes.hint;
46849
- }
46850
- if (typeof node === "object") {
46851
- const valueShapes = {};
46852
- Object.keys(node).forEach(key => {
46853
- valueShapes[key] = inferItemTreeShape(node[key]);
46854
- });
46855
- return shapes.shape(valueShapes);
46856
- }
46857
- throw new perseusCore.PerseusError(`unexpected multi-item node ${JSON.stringify(node)}`, perseusCore.Errors.InvalidInput);
46858
- }
46859
-
46860
- /**
46861
- * Convert the given Item to an ItemTree, by unwrapping the `_multi` key.
46862
- */
46863
- function itemToTree(item) {
46864
- return item._multi;
46865
- }
46866
-
46867
- /**
46868
- * Convert the given ItemTree to an Item, by wrapping it in the `_multi` key.
46869
- */
46870
- function treeToItem(node) {
46871
- return {
46872
- _multi: node
46873
- };
46874
- }
46875
-
46876
- var isObject$1 = function (obj) {
46877
- return obj === Object(obj);
46878
- };
46879
- var merge = function () {
46880
- var obj = {};
46881
- for (var i = 0; i < arguments.length; i++) {
46882
- var source = arguments[i];
46883
- if (source) {
46884
- for (var prop in source) {
46885
- obj[prop] = source[prop];
46886
- }
46887
- }
46888
- }
46889
- return obj;
46890
- };
46891
- var clone = function (obj) {
46892
- if (!isObject$1(obj)) {
46893
- return obj;
46894
- }
46895
- return Array.isArray(obj) ? obj.slice() : merge(obj);
46896
- };
46897
-
46898
- /* TODO batch *all* mutations
46899
- * idea: freeze / thaw implementations for all types
46900
- * lens constructor thaws, freeze delegates to type's freeze
46901
- */
46902
-
46903
- // equivalents, without requiring it
46904
- // find the implementation to use for a given object
46905
- var dispatch = function (x) {
46906
- if (Array.isArray(x)) {
46907
- return arr;
46908
- } else if (isObject$1(x)) {
46909
- return obj;
46910
- } else if (typeof x === "string") {
46911
- return str;
46912
- }
46913
- };
46914
-
46915
- // This is underscore with a different name
46916
- var lens = function (obj) {
46917
- if (obj instanceof lens) {
46918
- return obj;
46919
- }
46920
- if (!(this instanceof lens)) {
46921
- return new lens(obj);
46922
- }
46923
- var ops = dispatch(obj);
46924
- this._wrapped = ops.thaw ? ops.thaw(obj) : obj;
46925
- };
46926
- lens.prototype.freeze = function () {
46927
- var obj = this._wrapped;
46928
- var ops = dispatch(obj);
46929
- return ops.freeze ? ops.freeze(obj) : obj;
46930
- };
46931
- lens.prototype.zoom = function (lensArr) {
46932
- if (this._zoomStack === undefined) {
46933
- this._zoomStack = [];
46934
- }
46935
- this._zoomStack.push({
46936
- zoom: lensArr,
46937
- wrapped: this._wrapped
46938
- });
46939
- this._wrapped = lens(this._wrapped).get(lensArr);
46940
- return this;
46941
- };
46942
- lens.prototype.deZoom = function () {
46943
- var frame = this._zoomStack.pop();
46944
- this._wrapped = lens(frame.wrapped).set(frame.zoom, this._wrapped).freeze();
46945
- return this;
46946
- };
46947
- lens.prototype.get = function (lensArr) {
46948
- var obj = this._wrapped;
46949
- for (var i = 0; i < lensArr.length; i++) {
46950
- obj = dispatch(obj).get(obj, lensArr[i]);
46951
- }
46952
- return obj;
46953
- };
46954
- lens.prototype.mod = function (lensArr, f) {
46955
- var obj = this._wrapped;
46956
- var newObj = clone(obj);
46957
- var ops = dispatch(obj);
46958
- if (lensArr.length === 0) {
46959
- this._wrapped = f(this._wrapped);
46960
- } else if (lensArr.length === 1) {
46961
- this._wrapped = ops.mod(newObj, lensArr[0], f);
46962
- } else {
46963
- var monocle = lensArr[0];
46964
- var shortLens = lensArr.slice(1);
46965
-
46966
- // newObj = ops.mod(obj[monocle], shortLens, f);
46967
-
46968
- newObj[monocle] = lens(obj[monocle]).mod(shortLens, f).freeze();
46969
- this._wrapped = newObj;
46970
- }
46971
- return this;
46972
- };
46973
-
46974
- // TODO - move to individual files
46975
- lens.prototype.merge = function (lensArr, props) {
46976
- this._wrapped = lens(this._wrapped).mod(lensArr, function (oldProps) {
46977
- return merge(oldProps, props);
46978
- }).freeze();
46979
- return this;
46980
- };
46981
-
46982
- // Lens must have length >= 1 or there would be nothing to return
46983
- lens.prototype.del = function (lensArr) {
46984
- var obj = this._wrapped;
46985
- var ops = dispatch(obj);
46986
- if (lensArr.length === 1) {
46987
- this._wrapped = ops.del(obj, lensArr[0]);
46988
- } else {
46989
- var monocle = lensArr[0];
46990
- var shortLens = lensArr.slice(1);
46991
- var newObj = clone(obj);
46992
- newObj[monocle] = lens(obj[monocle]).del(shortLens).freeze();
46993
- this._wrapped = newObj;
46994
- }
46995
- return this;
46996
- };
46997
- lens.prototype.set = function (lensArr, set) {
46998
- return this.mod(lensArr, function () {
46999
- return set;
47000
- });
47001
- };
47002
-
47003
- // ARRAY FUNCTIONS
47004
-
47005
- const arr = {
47006
- get: function (arr, monocle) {
47007
- return arr[monocle];
47008
- },
47009
- set: function (arr, monocle, val) {
47010
- var newArr = arr.splice();
47011
- newArr[monocle] = val;
47012
- return newArr;
47013
- },
47014
- mod: function (arr, monocle, f) {
47015
- var newArr = arr.slice();
47016
- newArr[monocle] = f(arr[monocle]);
47017
- return newArr;
47018
- },
47019
- del: function (arr, monocle) {
47020
- var newArr = arr.slice();
47021
- newArr.splice(monocle, 1);
47022
- return newArr;
47023
- }
47024
- };
47025
-
47026
- /*
47027
- // Lens must point to a member of an array. We'll insert into that array.
47028
- lens.prototype.insertAt = function(lensArr, toInsert) {
47029
- var obj = this._wrapped;
47030
-
47031
- var arrLens = lensArr.slice(0, -1);
47032
- var arr = lens(obj).get(arrLens).slice(); // slice to copy
47033
-
47034
- var arrIdx = lensArr[lensArr.length-1];
47035
- arr.splice(arrIdx, 0, toInsert);
47036
- return lens(obj).set(arrLens, arr);
47037
- };
47038
-
47039
- lens.prototype.insertBefore = lens.prototype.insertAt;
47040
- lens.prototype.insertAfter = function(lensArr, toInsert) {
47041
- var newLens = lensArr.slice();
47042
- newLens[newLens.length-1] += 1;
47043
- return lens(this._wrapped).insertAt(newLens, toInsert);
47044
- };
47045
- */
47046
-
47047
- // OBJECT FUNCTIONS
47048
-
47049
- const obj = {
47050
- get: function (obj, monocle) {
47051
- return obj[monocle];
47052
- },
47053
- set: function (obj, monocle, val) {
47054
- var newObj = clone(obj);
47055
- newObj[monocle] = val;
47056
- return newObj;
47057
- },
47058
- mod: function (obj, monocle, f) {
47059
- var newObj = clone(obj);
47060
- newObj[monocle] = f(obj[monocle]);
47061
- return newObj;
47062
- },
47063
- del: function (obj, monocle) {
47064
- var newObj = clone(obj);
47065
- delete newObj[monocle];
47066
- return newObj;
47067
- }
47068
- };
47069
-
47070
- // STRING FUNCTIONS
47071
-
47072
- const str = {
47073
- get: function (arr, monocle) {
47074
- return arr[monocle];
47075
- },
47076
- set: function (arr, monocle, val) {
47077
- var newArr = arr.splice();
47078
- newArr[monocle] = val;
47079
- return newArr;
47080
- },
47081
- mod: function (arr, monocle, f) {
47082
- var newArr = arr.splice();
47083
- newArr[monocle] = f(arr[monocle]);
47084
- return newArr;
47085
- },
47086
- del: function (arr, monocle) {
47087
- var newArr = arr.slice();
47088
- newArr.splice(monocle);
47089
- return newArr;
47090
- }
47091
- };
47092
-
47093
- // TODO(mdr)
47094
- // TODO(mdr)
47095
- // TODO(mdr)
47096
-
47097
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
47098
-
47099
- /**
47100
- * TODO(somewhatabstract, JIRA-XXXX):
47101
- * Some usage of this type somewhere is causing TypeScript to believe that these
47102
- * elements should be components. Don't know where that is, to fix it up
47103
- * properly. In reality, these types are just too hard to use and aren't
47104
- * really helping us out as I now have to suppress this rather than
47105
- * get value from the type. :(
47106
- */ // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
47107
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
47108
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
47109
- class MultiRenderer extends React__namespace.Component {
47110
- static contextType = PerseusI18nContext;
47111
- rendererDataTreeMapper;
47112
- getRenderersMapper;
47113
- constructor(props) {
47114
- super(props);
47115
- this.rendererDataTreeMapper = buildMapper()
47116
- // @ts-expect-error - TS2345 - Argument of type 'unknown' is not assignable to parameter of type 'ContentNode'.
47117
- .setContentMapper((c, _, p) => this._makeContentRendererData(c, p))
47118
- // @ts-expect-error - TS2345 - Argument of type 'unknown' is not assignable to parameter of type 'HintNode'.
47119
- .setHintMapper(h => this._makeHintRendererData(h)).setTagsMapper(t => null);
47120
-
47121
- // @ts-expect-error - TS2322 - Type 'TreeMapperForLeavesAndCollections<unknown, any, unknown, any, unknown, unknown>' is not assignable to type 'TreeMapper<ContentRendererData, ContentRendererElement, HintRendererData, HintRendererElement, null, null>'.
47122
- this.getRenderersMapper = buildMapper()
47123
- // @ts-expect-error - TS2571 - Object is of type 'unknown'.
47124
- .setContentMapper(c => c.makeRenderer())
47125
- // @ts-expect-error - TS2571 - Object is of type 'unknown'.
47126
- .setHintMapper(h => h.makeRenderer()).setArrayMapper((renderers, data, shape) => this._annotateRendererArray(renderers, data, shape));
47127
-
47128
- // Keep state in sync with props.
47129
- this.state = this._tryMakeRendererState(this.props);
47130
- }
47131
- UNSAFE_componentWillReceiveProps(nextProps) {
47132
- // Keep state in sync with props.
47133
- if (nextProps.item !== this.props.item) {
47134
- this.setState(this._tryMakeRendererState(nextProps));
47135
- }
47136
- }
47137
-
47138
- /**
47139
- * Attempt to build a State that includes a renderer tree corresponding to
47140
- * the item provided in props. On error, return a state with `renderError`
47141
- * set instead.
47142
- */
47143
- _tryMakeRendererState(props) {
47144
- try {
47145
- return {
47146
- rendererDataTree: this._makeRendererDataTree(props.item, props.shape),
47147
- renderError: null
47148
- };
47149
- } catch (e) {
47150
- Log.error("Error building tree state", perseusCore.Errors.Internal, {
47151
- cause: e
47152
- });
47153
- return {
47154
- rendererDataTree: null,
47155
- renderError: e
47156
- };
47157
- }
47158
- }
47159
- _handleSerializedStateUpdated = (path, newState) => {
47160
- const {
47161
- onSerializedStateUpdated
47162
- } = this.props;
47163
- if (onSerializedStateUpdated) {
47164
- const oldState = this._getSerializedState(this.props.serializedState);
47165
- onSerializedStateUpdated(lens(oldState).set(path, newState).freeze());
47166
- }
47167
- };
47168
-
47169
- /**
47170
- * Props that aren't directly used by the MultiRenderer are delegated to
47171
- * the underlying Renderers.
47172
- */
47173
- _getRendererProps() {
47174
- // `item`, `children`, and others are unused. I'm
47175
- // explicitly pulling them out of `this.props` so I don't pass them to
47176
- // `<Renderer>`. I'm not sure how else to do this.
47177
- const {
47178
- item: _,
47179
- children: __,
47180
- shape: ___,
47181
- serializedState: ____,
47182
- onSerializedStateUpdated: _____,
47183
- ...otherProps
47184
- } = this.props;
47185
- return {
47186
- ...otherProps,
47187
- strings: this.context.strings
47188
- };
47189
- }
47190
-
47191
- /**
47192
- * Construct a Renderer and a ref placeholder for the given ContentNode.
47193
- */
47194
- _makeContentRendererData(content, path) {
47195
- // NOTE(emily): The `findExternalWidgets` function here is computed
47196
- // inline and thus changes each time we run this function. If it
47197
- // were to change every render, it would cause the Renderer to
47198
- // re-render a lot more than is necessary. Don't re-compute this
47199
- // element unless it is necessary!
47200
- // HACK(mdr): TypeScript can't prove that this is a ContentRendererData,
47201
- // because of how we awkwardly construct it in order to obtain a
47202
- // circular reference. But it is, I promise.
47203
- const data = {
47204
- ref: null,
47205
- makeRenderer: null
47206
- };
47207
- const refFunc = e => data.ref = e;
47208
- const findExternalWidgets = criterion => this._findWidgets(data, criterion);
47209
- const handleSerializedState = state => this._handleSerializedStateUpdated(path, state);
47210
- data.makeRenderer = () =>
47211
- /*#__PURE__*/
47212
- /**
47213
- * TODO(somewhatabstract, JIRA-XXXX):
47214
- * `content` contains props that Renderer doesn't have. However,
47215
- * since the type for `content` is not exact, it's hard to know
47216
- * if this spread is including undocumented props so mapping
47217
- * one to one could introduce a bug. Need to work out the exact
47218
- * type for ContentNode and then fix this.
47219
- */
47220
- // @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">>'.
47221
- React__namespace.createElement(Renderer, _extends({}, this._getRendererProps(), content, {
47222
- ref: refFunc,
47223
- findExternalWidgets: findExternalWidgets,
47224
- serializedState: this.props.serializedState ? lens(this.props.serializedState).get(path) : null,
47225
- onSerializedStateUpdated: handleSerializedState
47226
- }));
47227
- return data;
47228
- }
47229
-
47230
- /**
47231
- * Construct a Renderer for the given HintNode, and keep track of the hint
47232
- * itself for future use, too.
47233
- */
47234
- _makeHintRendererData(hint) {
47235
- // TODO(mdr): Once HintsRenderer supports inter-widget communication,
47236
- // give it a ref. Until then, leave the ref null forever, to avoid
47237
- // confusing the findWidgets functions.
47238
- //
47239
- // NOTE(davidflanagan): As a partial step toward inter-widget
47240
- // communication we're going to pass a findExternalWidgets function
47241
- // (using a dummy data object). This allows passage-ref widgets in
47242
- // hints to use findWidget() to find the passage widgets they reference.
47243
- // Note that this is one-way only, however. It does not allow
47244
- // widgets in the question to find widgets in the hints, for example.
47245
- const findExternalWidgets = criterion => this._findWidgets({}, criterion);
47246
- return {
47247
- hint,
47248
- findExternalWidgets,
47249
- // _annotateRendererArray() needs this
47250
- ref: null,
47251
- makeRenderer: () => /*#__PURE__*/React__namespace.createElement(HintsRenderer, _extends({}, this._getRendererProps(), {
47252
- findExternalWidgets: findExternalWidgets
47253
- // Note(Jeremy): The MultiRenderer codebase types are
47254
- // slightly different from the rest of the codebase.
47255
- // Ideally `HintNode` would spread the `Hint` type into it,
47256
- // but there is a difference in optionality of `widgets`
47257
- // and `images` and that causes a huge cascade of type
47258
- // errors (they are truly optional, but most of our code
47259
- // assumes they're provided/set). Ignoring this for now.
47260
- // @ts-expect-error - TS2769 - Type 'HintNode' is not assignable to type 'Hint'.
47261
- ,
47262
- hints: [hint]
47263
- }))
47264
- };
47265
- }
47266
-
47267
- /**
47268
- * Construct a tree of interconnected RendererDatas, corresponding to the
47269
- * given item. Called in `_tryMakeRendererState`, in order to store this
47270
- * tree in the component state.
47271
- */
47272
- _makeRendererDataTree(item, shape) {
47273
- const itemTree = itemToTree(item);
47274
- return this.rendererDataTreeMapper.mapTree(itemTree, shape);
47275
- }
47276
-
47277
- /**
47278
- * Return all widgets that meet the given criterion, from all Renderers
47279
- * except the Renderer that triggered this call.
47280
- *
47281
- * This function is provided to each Renderer's `findExternalWidgets` prop,
47282
- * which enables widgets in different Renderers to discover each other and
47283
- * communicate.
47284
- */
47285
- _findWidgets(callingData, filterCriterion) {
47286
- const results = [];
47287
- this._mapRenderers(data => {
47288
- if (callingData !== data && data.ref) {
47289
- results.push(...data.ref.findInternalWidgets(filterCriterion));
47290
- }
47291
- });
47292
- return results;
47293
- }
47294
-
47295
- /**
47296
- * Copy the renderer tree, apply the given transformation to the leaf nodes
47297
- * and the optional given transformation to the array nodes, and return the
47298
- * result.
47299
- *
47300
- * Used to provide structured data to the call site (the Renderer tree on
47301
- * `render`, the Score tree on `getScores`, etc.), and to traverse the
47302
- * renderer tree even when we disregard the output (like in
47303
- * `_findWidgets`).
47304
- */
47305
- _mapRenderers(
47306
- // eslint-disable-next-line no-restricted-syntax
47307
- leafMapper
47308
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
47309
- ) {
47310
- const {
47311
- rendererDataTree
47312
- } = this.state;
47313
- if (!rendererDataTree) {
47314
- return null;
47315
- }
47316
- const mapper = buildMapper().setContentMapper(leafMapper).setHintMapper(leafMapper);
47317
- return mapper.mapTree(rendererDataTree, this.props.shape);
47318
- }
47319
- _scoreFromRef(ref) {
47320
- if (!ref) {
47321
- return null;
47322
- }
47323
- const [guess, score] = ref.guessAndScore();
47324
- let state;
47325
- if (ref.getSerializedState) {
47326
- state = ref.getSerializedState();
47327
- }
47328
- return keScoreFromPerseusScore(score, guess, state);
47329
- }
47330
-
47331
- /**
47332
- * Return a tree in the shape of the multi-item, with scores at each of
47333
- * the content nodes and `null` at the other leaf nodes.
47334
- */
47335
- getScores() {
47336
- return this._mapRenderers(data => this._scoreFromRef(data.ref));
47337
- }
47338
-
47339
- /**
47340
- * Return a single composite score for all rendered content nodes.
47341
- * The `guess` is a tree in the shape of the multi-item, with an individual
47342
- * guess at each content node and `null` at the other leaf nodes.
47343
- */
47344
- score() {
47345
- const scores = [];
47346
- const state = [];
47347
- const guess = this._mapRenderers(data => {
47348
- if (!data.ref) {
47349
- return null;
47350
- }
47351
- if (data.ref.getSerializedState) {
47352
- state.push(data.ref.getSerializedState());
47353
- }
47354
- scores.push(data.ref.score());
47355
- return data.ref?.getUserInput();
47356
- });
47357
- const combinedScore = scores.reduce(combineScores);
47358
- return keScoreFromPerseusScore(combinedScore, guess, state);
47359
- }
47360
-
47361
- /**
47362
- * Return a tree in the shape of the multi-item, with serialized state at
47363
- * each of the content nodes and `null` at the other leaf nodes.
47364
- *
47365
- * If the lastSerializedState argument is supplied, this function will fill
47366
- * in the state of not-currently-rendered content and hint nodes with the
47367
- * values from the previous serialized state. If no lastSerializedState is
47368
- * supplied, `null` will be returned for not-currently-rendered content and
47369
- * hint nodes.
47370
- */
47371
- _getSerializedState(lastSerializedState) {
47372
- return this._mapRenderers((data, _, path) => {
47373
- if (data.ref) {
47374
- return data.ref.getSerializedState();
47375
- }
47376
- if (lastSerializedState) {
47377
- return lens(lastSerializedState).get(path);
47378
- }
47379
- return null;
47380
- });
47381
- }
47382
-
47383
- /**
47384
- * Given a tree in the shape of the multi-item, with serialized state at
47385
- * each of the content nodes, restore each state to the corresponding
47386
- * renderer if currently mounted.
47387
- */
47388
- restoreSerializedState(serializedState, callback) {
47389
- // We want to call our async callback only once all of the childrens'
47390
- // callbacks have run. We add one to this counter before we call out to
47391
- // each renderer and decrement it when it runs our callback.
47392
- let numCallbacks = 0;
47393
- const countCallback = () => {
47394
- numCallbacks--;
47395
- if (callback && numCallbacks === 0) {
47396
- callback();
47397
- }
47398
- };
47399
- this._mapRenderers((data, _, path) => {
47400
- if (!data.ref) {
47401
- return;
47402
- }
47403
- const state = lens(serializedState).get(path);
47404
- if (!state) {
47405
- return;
47406
- }
47407
- numCallbacks++;
47408
- data.ref?.restoreSerializedState(state, countCallback);
47409
- });
47410
- }
47411
-
47412
- /**
47413
- * Given an array of renderers, if it happens to be an array of *hint*
47414
- * renderers, then attach a `firstN` method to the array, which allows the
47415
- * layout to render the hints together in one HintsRenderer.
47416
- */
47417
- _annotateRendererArray(renderers, rendererDatas, shape) {
47418
- if (shape.elementShape.type === "hint") {
47419
- // The shape says that these are HintRendererDatas, even though
47420
- // it's not provable at compile time, so perform a cast.
47421
- const hintRendererDatas = rendererDatas;
47422
- renderers = [...renderers];
47423
- renderers.firstN = n => /*#__PURE__*/React__namespace.createElement(HintsRenderer, _extends({}, this._getRendererProps(), {
47424
- findExternalWidgets: hintRendererDatas[0] ? hintRendererDatas[0].findExternalWidgets : undefined,
47425
- hints: hintRendererDatas.map(d => d.hint),
47426
- hintsVisible: n
47427
- }));
47428
- }
47429
- return renderers;
47430
- }
47431
-
47432
- /**
47433
- * Return a tree in the shape of the multi-item, with a Renderer at each
47434
- * content node and a HintRenderer at each hint node.
47435
- *
47436
- * This is generated by running each of the `makeRenderer` functions at the
47437
- * leaf nodes.
47438
- */
47439
- _getRenderers() {
47440
- return this.getRenderersMapper.mapTree(this.state.rendererDataTree, this.props.shape);
47441
- }
47442
- render() {
47443
- if (this.state.renderError) {
47444
- return /*#__PURE__*/React__namespace.createElement("div", {
47445
- className: aphrodite.css(styles.error)
47446
- }, this.context.strings.errorRendering({
47447
- error: String(this.state.renderError)
47448
- }));
47449
- }
47450
-
47451
- // Pass the renderer tree to the `children` function, which will
47452
- // determine the actual content of this component.
47453
- return /*#__PURE__*/React__namespace.createElement(DependenciesContext.Provider, {
47454
- value: this.props.dependencies
47455
- }, this.props.children({
47456
- renderers: this._getRenderers()
47457
- }));
47458
- }
47459
- }
47460
- const styles = aphrodite.StyleSheet.create({
47461
- error: {
47462
- color: "red"
47463
- }
47464
- });
47465
-
47466
- /**
47467
- * Utility functions to build React PropTypes for multi-items and shapes.
47468
- *
47469
- * If you're writing new components, though, consider using the Item and Shape
47470
- * types instead.
47471
- */
47472
- /**
47473
- * Return a PropType that accepts Items of the given shape, and rejects other
47474
- * objects.
47475
- *
47476
- * Usage: `propTypes: {item: buildPropTypeForShape(myShape)}`
47477
- */
47478
- function buildPropTypeForShape(shape) {
47479
- return PropTypes__default["default"].oneOfType([PropTypes__default["default"].shape({
47480
- _multi: buildTreePropTypeForShape(shape)
47481
- }), PropTypes__default["default"].oneOf([null, undefined])]);
47482
- }
47483
-
47484
- /**
47485
- * Return a PropType that accepts ItemTrees of the given shape, and rejects
47486
- * other objects.
47487
- */
47488
- function buildTreePropTypeForShape(shape) {
47489
- if (shape.type === "content") {
47490
- return PropTypes__default["default"].shape({
47491
- // TODO(mdr): Remove #LegacyContentNode support.
47492
- __type: PropTypes__default["default"].oneOf(["content", "item"]).isRequired,
47493
- content: PropTypes__default["default"].string,
47494
- images: PropTypes__default["default"].objectOf(PropTypes__default["default"].any),
47495
- widgets: PropTypes__default["default"].objectOf(PropTypes__default["default"].any)
47496
- });
47497
- }
47498
- if (shape.type === "hint") {
47499
- return PropTypes__default["default"].shape({
47500
- __type: PropTypes__default["default"].oneOf(["hint"]).isRequired,
47501
- content: PropTypes__default["default"].string,
47502
- images: PropTypes__default["default"].objectOf(PropTypes__default["default"].any),
47503
- widgets: PropTypes__default["default"].objectOf(PropTypes__default["default"].any),
47504
- replace: PropTypes__default["default"].bool
47505
- });
47506
- }
47507
- if (shape.type === "tags") {
47508
- return PropTypes__default["default"].arrayOf(PropTypes__default["default"].string.isRequired);
47509
- }
47510
- if (shape.type === "array") {
47511
- const elementPropType = buildTreePropTypeForShape(shape.elementShape);
47512
- return PropTypes__default["default"].arrayOf(elementPropType.isRequired);
47513
- }
47514
- if (shape.type === "object") {
47515
- const valueShapes = shape.shape;
47516
- const propTypeShape = {};
47517
- Object.keys(valueShapes).forEach(key => {
47518
- propTypeShape[key] = buildTreePropTypeForShape(valueShapes[key]).isRequired;
47519
- });
47520
- return PropTypes__default["default"].shape(propTypeShape);
47521
- }
47522
- throw new perseusCore.PerseusError(
47523
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
47524
- `unexpected shape type ${shape.type}`, perseusCore.Errors.InvalidInput);
47525
- }
47526
-
47527
- /**
47528
- * This library provides support for Perseus multi-items: structured Perseus
47529
- * content that content creators can easily create, and that applications can
47530
- * easily render into different parts of the layout.
47531
- *
47532
- * For more details about application and motivation, see:
47533
- * https://sites.google.com/a/khanacademy.org/forge/for-developers/perseus-items-and-multi-items
47534
- *
47535
- * This file primarily exposes the `MultiRenderer` component, which performs
47536
- * multi-rendering. To multi-render a question, pass in the content of the item
47537
- * to the `MultiRenderer` component as a props. Then, pass in a function which
47538
- * takes an object of renderers (in the same structure as the content), and
47539
- * return a render tree. The `MultiRenderer` component will allow you to
47540
- * combine scores, serialized state, etc. without having to manually call on
47541
- * each of the functions. It also handles inter-widgets requests between the
47542
- * different renderers.
47543
- * For more details, see `multi-items/multi-renderer.jsx`.
47544
- *
47545
- * Example:
47546
- *
47547
- * item = {_multi: {
47548
- * left: <content data>,
47549
- * right: [<content data>, <content data>],
47550
- * }}
47551
- * shape = shapes.shape({
47552
- * left: shapes.content,
47553
- * right: shapes.arrayOf(shapes.content),
47554
- * })
47555
- *
47556
- * <MultiRenderer item={item} shape={shape}>
47557
- * {({renderers}) =>
47558
- * <div>
47559
- * <div id="left">{renderers.left}</div>
47560
- * <ul id="right">
47561
- * {renderers.right.map(r => <li>{r}</li>)}
47562
- * </ul>
47563
- * </div>
47564
- * }
47565
- * </MultiRenderer>
47566
- *
47567
- * This file also exposes `shapes`, which helps you construct a runtime type
47568
- * declaration for your particular class of multi-item. This can then be used
47569
- * to create a MultirendererEditor for your multi-item shape, and to validate
47570
- * that a multi-item conforms to the shape via `buildPropTypeForShape`.
47571
- * For more details, see `multi-items/shapes.js`.
47572
- *
47573
- * This file also exposes some utility functions for working with generic
47574
- * multi-items, like `findContentNodesInItem`, `findHintNodesInItem`,
47575
- * `inferItemShape`, and `buildEmptyItemForShape`.
47576
- * For more details, see `multi-items/items.js`.
47577
- */
47578
- var MultiItems = {
47579
- // Tools for rendering your multi-items
47580
- MultiRenderer,
47581
- // Tools for declaring your multi-item shapes
47582
- shapes,
47583
- buildPropTypeForShape,
47584
- // Tools for generically manipulating multi-items
47585
- buildEmptyItemForShape,
47586
- findContentNodesInItem: findContentNodesInItem$2,
47587
- findHintNodesInItem,
47588
- inferItemShape: inferItemShape$2
47589
- };
47590
-
47591
46470
  /**
47592
46471
  * Calculates whether a perseus item is renderable by a specific
47593
46472
  * perseus-item-data version.
@@ -47597,10 +46476,6 @@ var MultiItems = {
47597
46476
  * This supports widgets that contain renderers, such as the
47598
46477
  * group or sequence widgets.
47599
46478
  */
47600
- const {
47601
- findContentNodesInItem: findContentNodesInItem$1,
47602
- inferItemShape: inferItemShape$1
47603
- } = MultiItems;
47604
46479
  const isUpgradedWidgetInfoRenderableBy = function (widgetInfo, widgetRendererVersion) {
47605
46480
  if (widgetRendererVersion == null) {
47606
46481
  // If the widget does not exist in this version, this will
@@ -47646,17 +46521,6 @@ const isItemRenderableByVersion = function (itemData, rendererContentVersion) {
47646
46521
  if (itemData == null || rendererContentVersion == null) {
47647
46522
  throw new perseusCore.PerseusError("missing parameter to Perseus.isRenderable.item", perseusCore.Errors.InvalidInput);
47648
46523
  }
47649
- if (itemData._multi) {
47650
- const shape = inferItemShape$1(itemData);
47651
- let isRenderable = true;
47652
- findContentNodesInItem$1(itemData, shape, node => {
47653
- const nodeIsRenderable = isRendererContentRenderableBy(node, rendererContentVersion);
47654
- if (!nodeIsRenderable) {
47655
- isRenderable = false;
47656
- }
47657
- });
47658
- return isRenderable;
47659
- }
47660
46524
  return isRendererContentRenderableBy(itemData.question, rendererContentVersion);
47661
46525
  };
47662
46526
 
@@ -47664,10 +46528,6 @@ const isItemRenderableByVersion = function (itemData, rendererContentVersion) {
47664
46528
  * Identifies whether or not a given perseus item requires the use of a mouse
47665
46529
  * or screen, based on the widgets it contains.
47666
46530
  */
47667
- const {
47668
- findContentNodesInItem,
47669
- inferItemShape
47670
- } = MultiItems;
47671
46531
 
47672
46532
  // Iterate over a single Perseus renderer, mutating `widgets` by appending
47673
46533
  // violating widget types discovered in this item.
@@ -47688,12 +46548,7 @@ function traverseRenderer(itemData, widgets) {
47688
46548
  function violatingWidgets(itemData) {
47689
46549
  // TODO(jordan): Hints as well
47690
46550
  const widgets = [];
47691
- if (itemData._multi) {
47692
- const shape = inferItemShape(itemData);
47693
- findContentNodesInItem(itemData, shape, content => traverseRenderer(content, widgets));
47694
- } else {
47695
- traverseRenderer(itemData.question, widgets);
47696
- }
46551
+ traverseRenderer(itemData.question, widgets);
47697
46552
 
47698
46553
  // Uniquify the list of widgets (by type)
47699
46554
  return ___default["default"].uniq(widgets);
@@ -50216,6 +49071,10 @@ const ScoringUtil = {
50216
49071
  keScoreFromPerseusScore
50217
49072
  };
50218
49073
 
49074
+ /**
49075
+ * Types
49076
+ */
49077
+
50219
49078
  exports.ApiOptions = ApiOptions;
50220
49079
  exports.ArticleRenderer = ArticleRenderer;
50221
49080
  exports.BaseRadio = BaseRadio;
@@ -50238,7 +49097,6 @@ exports.KhanMath = KhanMath;
50238
49097
  exports.LoadingContext = context;
50239
49098
  exports.Log = Log;
50240
49099
  exports.MatrixWidget = Matrix$1;
50241
- exports.MultiItems = MultiItems;
50242
49100
  exports.NumericInput = NumericInput$1;
50243
49101
  exports.PerseusExpressionAnswerFormConsidered = PerseusExpressionAnswerFormConsidered;
50244
49102
  exports.PerseusI18nContext = PerseusI18nContext;
@@ -50257,8 +49115,6 @@ exports.Widgets = widgets$1;
50257
49115
  exports.addWidget = addWidget;
50258
49116
  exports.apiVersion = apiVersion;
50259
49117
  exports.bodyXsmallBold = bodyXsmallBold;
50260
- exports.buildEmptyItemTreeForShape = buildEmptyItemTreeForShape;
50261
- exports.buildMapper = buildMapper;
50262
49118
  exports.components = components;
50263
49119
  exports.containerSizeClass = containerSizeClass;
50264
49120
  exports.contentHasWidgetType = contentHasWidgetType;
@@ -50302,7 +49158,6 @@ exports.isItemRenderableByVersion = isItemRenderableByVersion;
50302
49158
  exports.isWidgetIdInContent = isWidgetIdInContent;
50303
49159
  exports.isWrongAnswerSupported = isWrongAnswerSupported;
50304
49160
  exports.itemDataVersion = itemDataVersion;
50305
- exports.itemToTree = itemToTree;
50306
49161
  exports.itemVersion = ItemVersion;
50307
49162
  exports.libVersion = libVersion;
50308
49163
  exports.lockedFigureColors = lockedFigureColors;
@@ -50315,7 +49170,6 @@ exports.plotterPlotTypes = plotterPlotTypes;
50315
49170
  exports.preprocessTex = preprocessTex;
50316
49171
  exports.registerAllWidgetsForTesting = registerAllWidgetsForTesting;
50317
49172
  exports.scorePerseusItem = scorePerseusItem;
50318
- exports.shapes = shapes;
50319
49173
  exports.shouldHaveIndividualAnswer = shouldHaveIndividualAnswer;
50320
49174
  exports.traverse = traverse;
50321
49175
  exports.usePerseusI18n = usePerseusI18n;