@khanacademy/perseus 12.2.0 → 12.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/article-renderer.d.ts +3 -2
  3. package/dist/error-boundary.d.ts +1 -0
  4. package/dist/es/index.css +4 -69
  5. package/dist/es/index.js +135 -78
  6. package/dist/es/index.js.map +1 -1
  7. package/dist/index.css +4 -69
  8. package/dist/index.js +130 -67
  9. package/dist/index.js.map +1 -1
  10. package/dist/renderer.d.ts +4 -1
  11. package/dist/styles/constants.d.ts +2 -2
  12. package/dist/widget-container.d.ts +2 -0
  13. package/dist/widgets/expression.d.ts +5 -4
  14. package/package.json +8 -8
  15. package/src/__tests__/renderer-api.test.tsx +18 -23
  16. package/src/__tests__/widget-container.test.tsx +126 -0
  17. package/src/article-renderer.tsx +15 -13
  18. package/src/error-boundary.tsx +4 -0
  19. package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +5 -5
  20. package/src/renderer.tsx +29 -27
  21. package/src/server-item-renderer.tsx +1 -0
  22. package/src/styles/articles.less +3 -1
  23. package/src/styles/constants.ts +4 -4
  24. package/src/styles/perseus-renderer.less +1 -11
  25. package/src/styles/variables.less +2 -2
  26. package/src/styles/variables.shared.less +2 -2
  27. package/src/styles/widgets/expression.less +0 -77
  28. package/src/styles/widgets/passage.less +1 -19
  29. package/src/widget-container.tsx +33 -10
  30. package/src/widgets/__stories__/label-image.stories.tsx +30 -0
  31. package/src/widgets/__testdata__/label-image.testdata.ts +63 -0
  32. package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +50 -19
  33. package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +5 -5
  34. package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +28 -28
  35. package/src/widgets/__tests__/expression.test.tsx +12 -9
  36. package/src/widgets/__tests__/renderQuestion.tsx +30 -13
  37. package/src/widgets/expression.tsx +19 -25
  38. package/src/widgets/graded-group-set.tsx +70 -25
  39. package/src/widgets/transformer.tsx +1 -2
  40. package/tsconfig-build.tsbuildinfo +1 -1
package/dist/index.css CHANGED
@@ -1742,11 +1742,11 @@ body.mobile ul.inequalities-one-line-radios > li {
1742
1742
  .framework-perseus.perseus-mobile .clearfix > .perseus-renderer {
1743
1743
  margin-bottom: 32px;
1744
1744
  }
1745
- .framework-perseus.perseus-mobile .perseus-renderer > .paragraph ul:not(.perseus-widget-radio) {
1745
+ .framework-perseus.perseus-mobile .perseus-renderer > .paragraph ul:not(.perseus-widget-radio, .indicatorContainer) {
1746
1746
  margin: 0 0 0 1em;
1747
1747
  padding: 0;
1748
1748
  }
1749
- .framework-perseus.perseus-mobile .perseus-renderer > .paragraph ul:not(.perseus-widget-radio) > li {
1749
+ .framework-perseus.perseus-mobile .perseus-renderer > .paragraph ul:not(.perseus-widget-radio, .indicatorContainer) > li {
1750
1750
  padding-left: 10px;
1751
1751
  margin-bottom: 24px;
1752
1752
  }
@@ -2394,7 +2394,7 @@ img.zoom-img {
2394
2394
  .framework-perseus .perseus-renderer > .paragraph > ol {
2395
2395
  margin: -11px 0px 22px 0px;
2396
2396
  }
2397
- .framework-perseus .paragraph ul:not(.perseus-widget-radio) {
2397
+ .framework-perseus .paragraph ul:not(.perseus-widget-radio, .indicatorContainer) {
2398
2398
  padding-left: 35px;
2399
2399
  list-style-type: disc;
2400
2400
  }
@@ -2684,71 +2684,6 @@ body.mobile .categorizer-container td.category input[type="radio"] + span:active
2684
2684
  #answer_area .perseus-widget-expression .error-tooltip .tooltipContainer > div:first-child {
2685
2685
  visibility: hidden !important;
2686
2686
  }
2687
- .perseus-widget-expression-old,
2688
- .perseus-widget-expression-old > .output,
2689
- .perseus-widget-expression-old > .output > .tex,
2690
- .perseus-widget-expression-old > .output > .placeholder {
2691
- display: block;
2692
- }
2693
- .perseus-widget-expression-old input,
2694
- #answer_area .perseus-widget-expression-old input {
2695
- direction: ltr;
2696
- border: 1px solid #a4a4a4;
2697
- border-radius: 5px;
2698
- box-sizing: border-box;
2699
- font-size: 14px;
2700
- margin-bottom: 5px;
2701
- max-width: 240px;
2702
- padding: 6px;
2703
- width: 100%;
2704
- }
2705
- .perseus-widget-expression-old > .output {
2706
- background: #f2f2f2;
2707
- border-radius: 5px;
2708
- padding: 10px;
2709
- margin: 10px 0;
2710
- }
2711
- #answer_area .perseus-widget-expression-old > .output {
2712
- background: #f7f7f7;
2713
- }
2714
- .perseus-widget-expression-old > .output > .tex {
2715
- overflow-x: auto;
2716
- padding: 5px;
2717
- }
2718
- .perseus-widget-expression-old > .output > .placeholder {
2719
- position: relative;
2720
- height: 40px;
2721
- overflow-y: hidden;
2722
- }
2723
- .perseus-widget-expression-old > .output > .placeholder > .error {
2724
- border-radius: 10px;
2725
- background: #f7f7f7;
2726
- border: 1px solid #ddd;
2727
- box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5);
2728
- color: #000;
2729
- display: none;
2730
- font-weight: normal;
2731
- min-height: 22px;
2732
- width: 168px;
2733
- position: absolute;
2734
- top: 0px;
2735
- left: 40px;
2736
- margin: auto;
2737
- }
2738
- .perseus-widget-expression-old > .output > .placeholder > .error > .buddy {
2739
- background-image: url(/images/perseus/error-buddy.png);
2740
- background-size: 100%;
2741
- height: 36px;
2742
- width: 40px;
2743
- position: absolute;
2744
- top: -3px;
2745
- left: -42px;
2746
- }
2747
- .perseus-widget-expression-old > .output > .placeholder > .error > .message {
2748
- font: 12px inherit;
2749
- line-height: 1.4em;
2750
- margin: 0px 4px;
2751
- }
2752
2687
  .perseus-widget-grapher {
2753
2688
  box-sizing: content-box;
2754
2689
  padding: 25px 25px 0 0;
@@ -3121,7 +3056,7 @@ body.mobile .perseus-matrix .math-output {
3121
3056
  }
3122
3057
  .framework-perseus.perseus-article .perseus-renderer .perseus-widget-passage-container .perseus-widget-passage div.paragraph,
3123
3058
  .perseus-widget-passage-container .perseus-widget-passage div.paragraph {
3124
- font-family: "minion-pro", KaTeX_Main, Times, "Times New Roman", serif;
3059
+ font-family: KaTeX_Main, Times, "Times New Roman", serif;
3125
3060
  }
3126
3061
  .framework-perseus.perseus-article .perseus-renderer .perseus-widget-passage-container .perseus-widget-passage .passage-title div.paragraph,
3127
3062
  .perseus-widget-passage-container .perseus-widget-passage .passage-title div.paragraph {
package/dist/index.js CHANGED
@@ -2007,11 +2007,26 @@ class Expression extends React__namespace.Component {
2007
2007
  });
2008
2008
  }
2009
2009
  });
2010
- _defineProperty(this, "simpleValidate", (rubric, onInputError) => {
2011
- onInputError = onInputError || function () {};
2012
- const result = Expression.validate(this.getUserInput(), rubric, onInputError);
2013
- this.sendExpressionEvaluatedEvent(result.type === "invalid" ? "invalid" : result.earned === result.total ? "correct" : "incorrect");
2014
- return result;
2010
+ _defineProperty(this, "simpleValidate", (_ref, onInputError) => {
2011
+ let {
2012
+ scoring,
2013
+ ...rubric
2014
+ } = _ref;
2015
+ const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {});
2016
+
2017
+ // "scoring" is a flag that indicates when we are checking answers.
2018
+ // otherwise, we may just be checking validity after changes.
2019
+ if (scoring && score.type !== "invalid") {
2020
+ var _this$props$analytics;
2021
+ (_this$props$analytics = this.props.analytics) === null || _this$props$analytics === void 0 ? void 0 : _this$props$analytics.onAnalyticsEvent({
2022
+ type: "perseus:expression-evaluated",
2023
+ payload: {
2024
+ result: score.earned === score.total ? "correct" : "incorrect",
2025
+ virtualKeypadVersion: deriveKeypadVersion(this.props.apiOptions)
2026
+ }
2027
+ });
2028
+ }
2029
+ return score;
2015
2030
  });
2016
2031
  _defineProperty(this, "getUserInput", () => {
2017
2032
  return Expression.getUserInputFromProps(this.props);
@@ -2036,8 +2051,8 @@ class Expression extends React__namespace.Component {
2036
2051
  this.props.trackInteraction();
2037
2052
  });
2038
2053
  _defineProperty(this, "_handleFocus", () => {
2039
- var _this$props$analytics;
2040
- (_this$props$analytics = this.props.analytics) === null || _this$props$analytics === void 0 ? void 0 : _this$props$analytics.onAnalyticsEvent({
2054
+ var _this$props$analytics2;
2055
+ (_this$props$analytics2 = this.props.analytics) === null || _this$props$analytics2 === void 0 ? void 0 : _this$props$analytics2.onAnalyticsEvent({
2041
2056
  type: "perseus:expression-focused",
2042
2057
  payload: null
2043
2058
  });
@@ -2244,16 +2259,6 @@ class Expression extends React__namespace.Component {
2244
2259
  // @ts-expect-error - TS2339 - Property 'insert' does not exist on type 'ReactInstance'.
2245
2260
  this.refs.input.insert(keyPressed);
2246
2261
  }
2247
- sendExpressionEvaluatedEvent(result) {
2248
- var _this$props$analytics2;
2249
- (_this$props$analytics2 = this.props.analytics) === null || _this$props$analytics2 === void 0 ? void 0 : _this$props$analytics2.onAnalyticsEvent({
2250
- type: "perseus:expression-evaluated",
2251
- payload: {
2252
- result,
2253
- virtualKeypadVersion: deriveKeypadVersion(this.props.apiOptions)
2254
- }
2255
- });
2256
- }
2257
2262
  render() {
2258
2263
  var _this$props$keypadCon, _this$props$analytics3;
2259
2264
  if (this.props.apiOptions.customKeypad) {
@@ -3739,10 +3744,10 @@ Zoom.prototype.dispose = function () {
3739
3744
  };
3740
3745
  const ZoomService = new ZoomServiceClass();
3741
3746
 
3742
- // @baseFontFamily: "Proxima Nova", sans-serif;
3743
- const baseFontFamily$1 = "'Proxima Nova',sans-serif";
3744
- // @boldFontFamily: "Proxima Nova Semibold", sans-serif;
3745
- const boldFontFamily = "'Proxima Nova Semibold',sans-serif";
3747
+ // @baseFontFamily: "Lato", sans-serif;
3748
+ const baseFontFamily$1 = "'Lato', sans-serif";
3749
+ // @boldFontFamily: "Lato-Bold", "Lato", sans-serif;
3750
+ const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
3746
3751
  // @green: #76a005;
3747
3752
  const green$1 = "#76A005";
3748
3753
  // @kaGreen: #71b307;
@@ -17392,9 +17397,11 @@ class ErrorBoundary extends React__namespace.Component {
17392
17397
  };
17393
17398
  }
17394
17399
  componentDidCatch(error, info) {
17400
+ var _this$props$onError, _this$props;
17395
17401
  this.setState({
17396
17402
  error: error.toString()
17397
17403
  });
17404
+ (_this$props$onError = (_this$props = this.props).onError) === null || _this$props$onError === void 0 ? void 0 : _this$props$onError.call(_this$props, error, info);
17398
17405
  Log.error("Perseus error boundary caught error", Errors.Internal, {
17399
17406
  cause: error,
17400
17407
  loggedMetadata: {
@@ -18763,6 +18770,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
18763
18770
  class WidgetContainer extends React__namespace.Component {
18764
18771
  constructor() {
18765
18772
  super(...arguments);
18773
+ _defineProperty(this, "widgetRef", /*#__PURE__*/React__namespace.createRef());
18766
18774
  _defineProperty(this, "state", {
18767
18775
  // TODO(benkomalo): before we're mounted, we don't know how big
18768
18776
  // we're going to be, so just default to MEDIUM for now. :/ In the
@@ -18772,8 +18780,7 @@ class WidgetContainer extends React__namespace.Component {
18772
18780
  widgetProps: this.props.initialProps
18773
18781
  });
18774
18782
  _defineProperty(this, "getWidget", () => {
18775
- // eslint-disable-next-line react/no-string-refs
18776
- return this.refs.widget;
18783
+ return this.widgetRef.current;
18777
18784
  });
18778
18785
  _defineProperty(this, "replaceWidgetProps", newWidgetProps => {
18779
18786
  this.setState({
@@ -18868,14 +18875,31 @@ class WidgetContainer extends React__namespace.Component {
18868
18875
  return /*#__PURE__*/React__namespace.createElement("div", {
18869
18876
  className: className,
18870
18877
  style: isStatic ? staticContainerStyles : {}
18871
- }, /*#__PURE__*/React__namespace.createElement(WidgetType, _extends({}, this.state.widgetProps, {
18872
- linterContext: linterContext,
18873
- containerSizeClass: this.state.sizeClass
18874
- // eslint-disable-next-line react/no-string-refs
18875
- ,
18876
- ref: "widget"
18877
- })), isStatic && /*#__PURE__*/React__namespace.createElement("div", {
18878
- style: staticOverlayStyles
18878
+ }, /*#__PURE__*/React__namespace.createElement(DependenciesContext.Consumer, null, _ref => {
18879
+ let {
18880
+ analytics
18881
+ } = _ref;
18882
+ return /*#__PURE__*/React__namespace.createElement(ErrorBoundary, {
18883
+ metadata: {
18884
+ widget_type: type,
18885
+ widget_id: this.props.id
18886
+ },
18887
+ onError: () => {
18888
+ analytics.onAnalyticsEvent({
18889
+ type: "perseus:widget-rendering-error",
18890
+ payload: {
18891
+ widgetType: type,
18892
+ widgetId: this.props.id
18893
+ }
18894
+ });
18895
+ }
18896
+ }, /*#__PURE__*/React__namespace.createElement(WidgetType, _extends({}, this.state.widgetProps, {
18897
+ linterContext: linterContext,
18898
+ containerSizeClass: this.state.sizeClass,
18899
+ ref: this.widgetRef
18900
+ })), isStatic && /*#__PURE__*/React__namespace.createElement("div", {
18901
+ style: staticOverlayStyles
18902
+ }));
18879
18903
  }));
18880
18904
  }
18881
18905
  UNSAFE_componentWillReceiveProps(nextProps) {
@@ -18899,6 +18923,7 @@ const rContainsNonWhitespace = /\S/;
18899
18923
  const rImageURL = /(web\+graphie|https):\/\/[^\s]*/;
18900
18924
  const noopOnRender = () => {};
18901
18925
  const SHOULD_CLEAR_WIDGETS_PROP_LIST = ["content", "problemNum", "widgets"];
18926
+ const makeContainerId = id => "container:" + id;
18902
18927
 
18903
18928
  // Check if one focus path / id path is a prefix of another
18904
18929
  // The focus path null will never be a prefix of any non-null
@@ -18945,6 +18970,7 @@ class Renderer extends React__namespace.Component {
18945
18970
  _defineProperty(this, "_isMounted", void 0);
18946
18971
  _defineProperty(this, "_isTwoColumn", void 0);
18947
18972
  _defineProperty(this, "_translationLinter", void 0);
18973
+ _defineProperty(this, "_widgetContainers", new Map());
18948
18974
  _defineProperty(this, "lastRenderedMarkdown", void 0);
18949
18975
  _defineProperty(this, "reuseMarkdown", void 0);
18950
18976
  _defineProperty(this, "translationIndex", void 0);
@@ -19018,19 +19044,22 @@ class Renderer extends React__namespace.Component {
19018
19044
  // By this point we should have no duplicates, which are
19019
19045
  // filtered out in this.render(), so we shouldn't have to
19020
19046
  // worry about using this widget key and ref:
19021
- return /*#__PURE__*/React__namespace.createElement(ErrorBoundary, {
19022
- key: "container:" + id,
19023
- metadata: {
19024
- widget_type: type,
19025
- widget_id: id
19026
- }
19027
- }, /*#__PURE__*/React__namespace.createElement(WidgetContainer, {
19028
- ref: "container:" + id,
19047
+ return /*#__PURE__*/React__namespace.createElement(WidgetContainer, {
19048
+ key: makeContainerId(id),
19049
+ id: id,
19050
+ ref: node => {
19051
+ const containerId = makeContainerId(id);
19052
+ if (node != null) {
19053
+ this._widgetContainers.set(containerId, node);
19054
+ } else {
19055
+ this._widgetContainers.delete(containerId);
19056
+ }
19057
+ },
19029
19058
  type: type,
19030
19059
  initialProps: this.getWidgetProps(id),
19031
19060
  shouldHighlight: shouldHighlight,
19032
19061
  linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "widget")
19033
- }));
19062
+ });
19034
19063
  }
19035
19064
  return null;
19036
19065
  });
@@ -19058,7 +19087,6 @@ class Renderer extends React__namespace.Component {
19058
19087
  }
19059
19088
  return {
19060
19089
  ...widgetProps,
19061
- ref: id,
19062
19090
  widgetId: id,
19063
19091
  alignment: widgetInfo && widgetInfo.alignment,
19064
19092
  // When determining if a widget is static, we verify that the widget is not an
@@ -19191,12 +19219,10 @@ class Renderer extends React__namespace.Component {
19191
19219
  return [...this.findInternalWidgets(filterCriterion), ...this.props.findExternalWidgets(filterCriterion)];
19192
19220
  });
19193
19221
  _defineProperty(this, "getWidgetInstance", id => {
19194
- // eslint-disable-next-line react/no-string-refs
19195
- const ref = this.refs["container:" + id];
19222
+ const ref = this._widgetContainers.get(makeContainerId(id));
19196
19223
  if (!ref) {
19197
19224
  return null;
19198
19225
  }
19199
- // @ts-expect-error - TS2339 - Property 'getWidget' does not exist on type 'ReactInstance'.
19200
19226
  return ref.getWidget();
19201
19227
  });
19202
19228
  _defineProperty(this, "_onWidgetFocus", function (id) {
@@ -19910,7 +19936,10 @@ class Renderer extends React__namespace.Component {
19910
19936
  const widget = this.getWidgetInstance(id);
19911
19937
  // widget can be undefined if it hasn't yet been rendered
19912
19938
  if (widget && widget.simpleValidate) {
19913
- widgetScores[id] = widget.simpleValidate(props === null || props === void 0 ? void 0 : props.options, onInputError);
19939
+ widgetScores[id] = widget.simpleValidate({
19940
+ ...(props === null || props === void 0 ? void 0 : props.options),
19941
+ scoring: true
19942
+ }, onInputError);
19914
19943
  }
19915
19944
  });
19916
19945
  return widgetScores;
@@ -20061,9 +20090,7 @@ class Renderer extends React__namespace.Component {
20061
20090
  // they are re-rendered, so even if they've been
20062
20091
  // re-rendered we need to call these methods on them.
20063
20092
  ___default["default"].each(this.widgetIds, id => {
20064
- // eslint-disable-next-line react/no-string-refs
20065
- const container = this.refs["container:" + id];
20066
- // @ts-expect-error - TS2339 - Property 'replaceWidgetProps' does not exist on type 'ReactInstance'.
20093
+ const container = this._widgetContainers.get(makeContainerId(id));
20067
20094
  container && container.replaceWidgetProps(this.getWidgetProps(id));
20068
20095
  });
20069
20096
  if (this.props.serializedState && !___default["default"].isEqual(this.props.serializedState, this.getSerializedState())) {
@@ -25036,24 +25063,38 @@ class Indicators extends React__namespace.Component {
25036
25063
  }
25037
25064
  render() {
25038
25065
  return /*#__PURE__*/React__namespace.createElement("ul", {
25039
- className: aphrodite.css(styles$j.indicatorContainer)
25066
+ // reduntantly add class name for use in .less files--
25067
+ // the styles object key gets hashed
25068
+ className: classNames__default["default"](aphrodite.css(styles$j.indicatorContainer), "indicatorContainer")
25040
25069
  }, this.props.gradedGroups.map((_ref, i) => {
25041
25070
  let {
25042
25071
  title
25043
25072
  } = _ref;
25044
25073
  return /*#__PURE__*/React__namespace.createElement("li", {
25074
+ className: aphrodite.css(styles$j.indicator),
25075
+ key: title
25076
+ }, /*#__PURE__*/React__namespace.createElement(Clickable__default["default"], {
25045
25077
  role: "button",
25046
25078
  "aria-label": i18n__namespace._("Skip to %(title)s", {
25047
25079
  title
25048
25080
  }),
25049
- key: title,
25050
- className: aphrodite.css(styles$j.indicator, i === this.props.currentGroup && styles$j.selectedIndicator),
25051
- tabIndex: 0,
25081
+ style: styles$j.indicatorButton,
25052
25082
  onClick: () => this.props.onChangeCurrentGroup(i),
25053
25083
  onKeyDown: e => this.handleKeyDown(e, i)
25054
- }, i === this.props.currentGroup && /*#__PURE__*/React__namespace.createElement("span", {
25055
- className: aphrodite.css(a11y.srOnly)
25056
- }, i18n__namespace._("Current")));
25084
+ }, _ref2 => {
25085
+ let {
25086
+ hovered,
25087
+ focused,
25088
+ pressed
25089
+ } = _ref2;
25090
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
25091
+ style: [styles$j.indicatorDot, (hovered || focused || pressed) && styles$j.indicatorDotFocused]
25092
+ }, i === this.props.currentGroup && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
25093
+ style: styles$j.indicatorDotActive
25094
+ }, /*#__PURE__*/React__namespace.createElement("span", {
25095
+ className: aphrodite.css(a11y.srOnly)
25096
+ }, i18n__namespace._("Current"))));
25097
+ }));
25057
25098
  }));
25058
25099
  }
25059
25100
  }
@@ -25207,19 +25248,42 @@ const styles$j = aphrodite.StyleSheet.create({
25207
25248
  display: "flex",
25208
25249
  flexDirection: "row",
25209
25250
  listStyle: "none",
25210
- margin: "unset"
25251
+ margin: "unset",
25252
+ paddingInlineStart: "unset",
25253
+ flexWrap: "wrap"
25211
25254
  },
25212
25255
  indicator: {
25256
+ width: 24,
25257
+ height: 24
25258
+ },
25259
+ indicatorButton: {
25260
+ width: "100%",
25261
+ height: "100%",
25262
+ display: "flex",
25263
+ flexWrap: "wrap",
25264
+ placeContent: "center",
25265
+ cursor: "pointer",
25266
+ ":focus": {
25267
+ outline: "none"
25268
+ }
25269
+ },
25270
+ indicatorDot: {
25271
+ boxSizing: "content-box",
25213
25272
  width: 10,
25214
25273
  height: 10,
25215
25274
  borderRadius: "100%",
25216
- border: "3px solid",
25275
+ borderWidth: 2,
25217
25276
  borderColor: Color__default["default"].blue,
25218
- marginLeft: 5,
25219
- cursor: "pointer"
25277
+ borderStyle: "solid"
25220
25278
  },
25221
- selectedIndicator: {
25222
- backgroundColor: Color__default["default"].blue
25279
+ indicatorDotFocused: {
25280
+ borderWidth: 5,
25281
+ borderStyle: "double"
25282
+ },
25283
+ indicatorDotActive: {
25284
+ backgroundColor: Color__default["default"].blue,
25285
+ width: "100%",
25286
+ height: "100%"
25223
25287
  },
25224
25288
  container: {
25225
25289
  borderTop: "1px solid ".concat(gray76),
@@ -42366,8 +42430,7 @@ class Transformer extends React__namespace.Component {
42366
42430
  // @ts-expect-error - TS2345 - Argument of type '{ type: "dilation"; center: any; scale: number; }' is not assignable to parameter of type 'PerseusTransformerTransformation'.
42367
42431
  self.doTransform({
42368
42432
  type: "dilation",
42369
- // NOTE(kevinb): It's hard to convince that this.dilationCircle
42370
- // will still be defined here. center: self.dilationCircle.centerPoint.coord,
42433
+ center: self.dilationCircle.centerPoint.coord,
42371
42434
  scale: newRadius / oldRadius
42372
42435
  });
42373
42436
  },
@@ -44015,7 +44078,7 @@ const init = function (options) {
44015
44078
 
44016
44079
  // This file is processed by a Rollup plugin (replace) to inject the production
44017
44080
  const libName = "@khanacademy/perseus";
44018
- const libVersion = "12.2.0";
44081
+ const libVersion = "12.4.0";
44019
44082
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
44020
44083
 
44021
44084
  const apiVersion = {
@@ -44160,7 +44223,7 @@ class ArticleRenderer extends React__namespace.Component {
44160
44223
  },
44161
44224
  linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "article"),
44162
44225
  legacyPerseusLint: this.props.legacyPerseusLint
44163
- })));
44226
+ }, this.props.dependencies)));
44164
44227
  });
44165
44228
  return /*#__PURE__*/React__namespace.createElement("div", {
44166
44229
  className: classes
@@ -45256,7 +45319,7 @@ class ServerItemRenderer extends React__namespace.Component {
45256
45319
  };
45257
45320
  const questionRenderer = /*#__PURE__*/React__namespace.createElement(context$1.Provider, {
45258
45321
  value: contextValue
45259
- }, /*#__PURE__*/React__namespace.createElement(Renderer, {
45322
+ }, /*#__PURE__*/React__namespace.createElement(Renderer, _extends({
45260
45323
  keypadElement: this.props.keypadElement,
45261
45324
  problemNum: this.props.problemNum,
45262
45325
  onInteractWithWidget: this.handleInteractWithWidget,
@@ -45272,7 +45335,7 @@ class ServerItemRenderer extends React__namespace.Component {
45272
45335
  content: this.props.item.question.content,
45273
45336
  widgets: this.props.item.question.widgets,
45274
45337
  images: this.props.item.question.images
45275
- }));
45338
+ }, this.props.dependencies)));
45276
45339
  const hintsRenderer = /*#__PURE__*/React__namespace.createElement(HintsRenderer, {
45277
45340
  hints: this.props.item.hints,
45278
45341
  hintsVisible: this.props.hintsVisible,