@khanacademy/perseus-editor 5.2.14 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
2
- import { constants, components, Widgets, icons, WIDGET_PROP_DENYLIST, Util, Log, PerseusError, Errors, PerseusMarkdown, preprocessTex, ApiOptions, buildMapper, itemToTree, buildEmptyItemTreeForShape, shapes, ItemExtras, itemDataVersion, ClassNames, MultiItems, traverse, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, WidgetJsonifyDeprecated, PerseusExpressionAnswerFormConsidered, Expression, interactiveSizes, KhanMath, GrapherWidget, GrapherUtil, SizingUtils, KhanColors, ColorPicker, DashPicker, ArrowPicker, ElementContainer, InteractiveGraphWidget, globalStyles, globalConstants, MatrixWidget, PlotterWidget, plotterPlotTypes, BaseRadio, TableWidget, displaySigFigs, widgets } from '@khanacademy/perseus';
2
+ import { constants, components, Widgets, icons, WIDGET_PROP_DENYLIST, Util, Log, PerseusError, Errors, PerseusMarkdown, preprocessTex, ApiOptions, buildMapper, itemToTree, buildEmptyItemTreeForShape, shapes, ItemExtras, itemDataVersion, ClassNames, MultiItems, traverse, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, WidgetJsonifyDeprecated, PerseusExpressionAnswerFormConsidered, Expression, interactiveSizes, KhanMath, GrapherWidget, GrapherUtil, SizingUtils, PerseusI18nContext, KhanColors, ColorPicker, DashPicker, ArrowPicker, ElementContainer, lockedFigureColors, InteractiveGraphWidget, globalStyles, globalConstants, MatrixWidget, PlotterWidget, plotterPlotTypes, BaseRadio, TableWidget, displaySigFigs, widgets } from '@khanacademy/perseus';
3
3
  import * as React from 'react';
4
4
  import { useRef, useEffect } from 'react';
5
5
  import _ from 'underscore';
@@ -16,33 +16,32 @@ import classNames from 'classnames';
16
16
  import ReactDOM from 'react-dom';
17
17
  import { point, number } from '@khanacademy/kmath';
18
18
  import * as KAS from '@khanacademy/kas';
19
- import { isTruthy } from '@khanacademy/wonder-stuff-core';
20
- import { createMathField } from '@khanacademy/math-input';
19
+ import { isTruthy, UnreachableCaseError } from '@khanacademy/wonder-stuff-core';
20
+ import { useMathInputI18n, createMathField } from '@khanacademy/math-input';
21
21
  import { SingleSelect, OptionItem, ActionMenu, ActionItem } from '@khanacademy/wonder-blocks-dropdown';
22
22
  import { TextField, Checkbox as Checkbox$1 } from '@khanacademy/wonder-blocks-form';
23
- import { LabelSmall, LabelLarge, LabelMedium, BodyMonospace } from '@khanacademy/wonder-blocks-typography';
23
+ import { LabelSmall, LabelMedium, LabelLarge, BodyMonospace } from '@khanacademy/wonder-blocks-typography';
24
+ import { AccordionSection } from '@khanacademy/wonder-blocks-accordion';
24
25
  import IconButton from '@khanacademy/wonder-blocks-icon-button';
25
26
  import trashIcon from '@phosphor-icons/core/bold/trash-bold.svg';
27
+ import Switch from '@khanacademy/wonder-blocks-switch';
26
28
  import Button from '@khanacademy/wonder-blocks-button';
27
- import * as i18n$1 from '@khanacademy/wonder-blocks-i18n';
28
29
 
29
30
  // This file is processed by a Rollup plugin (replace) to inject the production
30
31
  const libName = "@khanacademy/perseus-editor";
31
- const libVersion = "5.2.14";
32
+ const libVersion = "6.0.0";
32
33
  addLibraryVersionToPerseusDebug(libName, libVersion);
33
34
 
34
35
  function _extends() {
35
36
  _extends = Object.assign ? Object.assign.bind() : function (target) {
36
37
  for (var i = 1; i < arguments.length; i++) {
37
38
  var source = arguments[i];
38
-
39
39
  for (var key in source) {
40
40
  if (Object.prototype.hasOwnProperty.call(source, key)) {
41
41
  target[key] = source[key];
42
42
  }
43
43
  }
44
44
  }
45
-
46
45
  return target;
47
46
  };
48
47
  return _extends.apply(this, arguments);
@@ -220,7 +219,7 @@ class SectionControlButton extends React.Component {
220
219
  role: "button",
221
220
  hideDefaultFocusRing: true,
222
221
  "aria-label": title,
223
- style: styles$q.button
222
+ style: styles$s.button
224
223
  }, ({
225
224
  hovered,
226
225
  focused,
@@ -228,7 +227,7 @@ class SectionControlButton extends React.Component {
228
227
  }) => /*#__PURE__*/React.createElement(InlineIcon$b, icon));
229
228
  }
230
229
  }
231
- const styles$q = StyleSheet.create({
230
+ const styles$s = StyleSheet.create({
232
231
  button: {
233
232
  backgroundColor: color.gold,
234
233
  margin: 2,
@@ -20750,19 +20749,19 @@ class TexErrorView extends React.Component {
20750
20749
  const {
20751
20750
  showErrors
20752
20751
  } = this.state;
20753
- const iconStyle = showErrors ? styles$p.iconExpanded : styles$p.iconCollapsed;
20752
+ const iconStyle = showErrors ? styles$r.iconExpanded : styles$r.iconCollapsed;
20754
20753
  return /*#__PURE__*/React.createElement(View, {
20755
- style: styles$p.errorContainer
20754
+ style: styles$r.errorContainer
20756
20755
  }, /*#__PURE__*/React.createElement(View, {
20757
- style: styles$p.title,
20756
+ style: styles$r.title,
20758
20757
  onClick: this.handleToggleTexErrors
20759
20758
  }, /*#__PURE__*/React.createElement(PhosphorIcon, {
20760
20759
  icon: caretRight,
20761
20760
  style: iconStyle
20762
20761
  }), "\xA0 TeX Errors (", errorList.length, ")"), showErrors && /*#__PURE__*/React.createElement(View, {
20763
- style: styles$p.errorExplanation
20762
+ style: styles$r.errorExplanation
20764
20763
  }, "If your math doesn't display correctly, these errors might help you troubleshoot. Message #content-kitchen for help."), showErrors && errorList.map((e, index) => /*#__PURE__*/React.createElement(View, {
20765
- style: styles$p.error,
20764
+ style: styles$r.error,
20766
20765
  key: index
20767
20766
  }, /*#__PURE__*/React.createElement(View, {
20768
20767
  style: {
@@ -20771,7 +20770,7 @@ class TexErrorView extends React.Component {
20771
20770
  }, e.math), /*#__PURE__*/React.createElement(View, null, e.message))));
20772
20771
  }
20773
20772
  }
20774
- const styles$p = StyleSheet.create({
20773
+ const styles$r = StyleSheet.create({
20775
20774
  title: {
20776
20775
  backgroundColor: "#eee",
20777
20776
  fontSize: "1.25em",
@@ -24336,13 +24335,11 @@ function _objectWithoutPropertiesLoose(source, excluded) {
24336
24335
  var target = {};
24337
24336
  var sourceKeys = Object.keys(source);
24338
24337
  var key, i;
24339
-
24340
24338
  for (i = 0; i < sourceKeys.length; i++) {
24341
24339
  key = sourceKeys[i];
24342
24340
  if (excluded.indexOf(key) >= 0) continue;
24343
24341
  target[key] = source[key];
24344
24342
  }
24345
-
24346
24343
  return target;
24347
24344
  }
24348
24345
 
@@ -24574,7 +24571,7 @@ class SimpleButton extends React.Component {
24574
24571
  } = _this$props,
24575
24572
  otherProps = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
24576
24573
  return /*#__PURE__*/React.createElement("div", _extends({
24577
- className: css(styles$o.baseButton, color === "green" && styles$o.green, color === "orange" && styles$o.orange),
24574
+ className: css(styles$q.baseButton, color === "green" && styles$q.green, color === "orange" && styles$q.orange),
24578
24575
  onClick: onClick
24579
24576
  }, otherProps), children);
24580
24577
  }
@@ -24589,7 +24586,7 @@ SimpleButton.defaultProps = {
24589
24586
  color: "green",
24590
24587
  onClick: () => {}
24591
24588
  };
24592
- const styles$o = StyleSheet.create({
24589
+ const styles$q = StyleSheet.create({
24593
24590
  baseButton: {
24594
24591
  top: "0",
24595
24592
  fontSize: "11px",
@@ -24809,23 +24806,23 @@ const LeafContainer = ({
24809
24806
  }) => {
24810
24807
  const hasPreviewHeading = shape.type === "content" || shape.type === "hint";
24811
24808
  const previewHeading = hasPreviewHeading && /*#__PURE__*/React.createElement("div", {
24812
- className: css(styles$n.containerHeader)
24809
+ className: css(styles$p.containerHeader)
24813
24810
  }, /*#__PURE__*/React.createElement(Header, {
24814
24811
  depth: path.length,
24815
- className: css(styles$n.containerTitle)
24812
+ className: css(styles$p.containerTitle)
24816
24813
  }, capitalize(name)));
24817
24814
  return /*#__PURE__*/React.createElement("div", {
24818
- className: css(styles$n.container)
24815
+ className: css(styles$p.container)
24819
24816
  }, /*#__PURE__*/React.createElement("span", {
24820
- className: css(styles$n.row, styles$n.rowHeading)
24817
+ className: css(styles$p.row, styles$p.rowHeading)
24821
24818
  }, /*#__PURE__*/React.createElement("div", {
24822
- className: css(styles$n.columnLeft)
24819
+ className: css(styles$p.columnLeft)
24823
24820
  }, /*#__PURE__*/React.createElement("div", {
24824
- className: "pod-title " + css(styles$n.containerHeader)
24821
+ className: "pod-title " + css(styles$p.containerHeader)
24825
24822
  }, /*#__PURE__*/React.createElement("div", {
24826
- className: css(styles$n.containerTitle)
24823
+ className: css(styles$p.containerTitle)
24827
24824
  }, capitalize(name)), controls)), /*#__PURE__*/React.createElement("div", {
24828
- className: css(styles$n.columnRight)
24825
+ className: css(styles$p.columnRight)
24829
24826
  }, previewHeading)), children);
24830
24827
  };
24831
24828
  const ArrayContainer = props => {
@@ -24838,11 +24835,11 @@ const ArrayContainer = props => {
24838
24835
  actions
24839
24836
  } = props;
24840
24837
  return /*#__PURE__*/React.createElement("div", {
24841
- className: css(styles$n.container)
24838
+ className: css(styles$p.container)
24842
24839
  }, controls && /*#__PURE__*/React.createElement("div", {
24843
- className: css(styles$n.columnLeft, styles$n.containerHeader)
24840
+ className: css(styles$p.columnLeft, styles$p.containerHeader)
24844
24841
  }, controls), /*#__PURE__*/React.createElement("div", null, children), /*#__PURE__*/React.createElement("div", {
24845
- className: css(styles$n.columnLeft)
24842
+ className: css(styles$p.columnLeft)
24846
24843
  }, /*#__PURE__*/React.createElement("a", {
24847
24844
  href: "javascript:void 0",
24848
24845
  onClick: () => actions.addArrayElement(path, shape.elementShape)
@@ -24855,28 +24852,28 @@ const ObjectContainer = ({
24855
24852
  path
24856
24853
  }) => {
24857
24854
  const headingEditor = /*#__PURE__*/React.createElement("div", {
24858
- className: css(styles$n.containerHeader)
24855
+ className: css(styles$p.containerHeader)
24859
24856
  }, /*#__PURE__*/React.createElement(Header, {
24860
24857
  depth: path.length,
24861
- className: css(styles$n.containerTitle)
24858
+ className: css(styles$p.containerTitle)
24862
24859
  }, capitalize(name)), controls);
24863
24860
  const headingPreview = (name || controls) && /*#__PURE__*/React.createElement("div", {
24864
- className: css(styles$n.containerHeader, styles$n.previewCollectionHeader)
24861
+ className: css(styles$p.containerHeader, styles$p.previewCollectionHeader)
24865
24862
  }, /*#__PURE__*/React.createElement(Header, {
24866
24863
  depth: path.length,
24867
- className: css(styles$n.containerTitle)
24864
+ className: css(styles$p.containerTitle)
24868
24865
  }, capitalize(name)));
24869
24866
  const hasBothHeadings = headingEditor && headingPreview;
24870
24867
  return /*#__PURE__*/React.createElement("div", {
24871
- className: css(styles$n.container)
24868
+ className: css(styles$p.container)
24872
24869
  }, hasBothHeadings && /*#__PURE__*/React.createElement("span", {
24873
- className: css(styles$n.row)
24870
+ className: css(styles$p.row)
24874
24871
  }, /*#__PURE__*/React.createElement("div", {
24875
- className: css(styles$n.columnLeft)
24872
+ className: css(styles$p.columnLeft)
24876
24873
  }, headingEditor), /*#__PURE__*/React.createElement("div", {
24877
- className: css(styles$n.columnRight)
24874
+ className: css(styles$p.columnRight)
24878
24875
  }, headingPreview)), /*#__PURE__*/React.createElement("div", {
24879
- className: css(path.length > 0 && styles$n.contentIndent)
24876
+ className: css(path.length > 0 && styles$p.contentIndent)
24880
24877
  }, children));
24881
24878
  };
24882
24879
 
@@ -24999,11 +24996,11 @@ const ItemNodeContent = withStickiness(props => {
24999
24996
  className: "framework-perseus"
25000
24997
  }, lens(renderers).get(path));
25001
24998
  return /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("div", {
25002
- className: css(styles$n.row)
24999
+ className: css(styles$p.row)
25003
25000
  }, /*#__PURE__*/React.createElement("div", {
25004
- className: css(styles$n.columnLeft)
25001
+ className: css(styles$p.columnLeft)
25005
25002
  }, /*#__PURE__*/React.createElement("div", {
25006
- className: css(sticky && styles$n.sticky)
25003
+ className: css(sticky && styles$p.sticky)
25007
25004
  },
25008
25005
  /*#__PURE__*/
25009
25006
  // @ts-expect-error - TS2769 - No overload matches this call.
@@ -25011,9 +25008,9 @@ const ItemNodeContent = withStickiness(props => {
25011
25008
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
25012
25009
  apiOptions: apiOptions
25013
25010
  })))), /*#__PURE__*/React.createElement("div", {
25014
- className: css(styles$n.columnRight)
25011
+ className: css(styles$p.columnRight)
25015
25012
  }, /*#__PURE__*/React.createElement("div", {
25016
- className: css(sticky && styles$n.sticky)
25013
+ className: css(sticky && styles$p.sticky)
25017
25014
  }, preview))));
25018
25015
  });
25019
25016
  const HintNodeContent = withStickiness(props => {
@@ -25029,13 +25026,13 @@ const HintNodeContent = withStickiness(props => {
25029
25026
  className: "framework-perseus"
25030
25027
  }, lens(renderers).get(path));
25031
25028
  return /*#__PURE__*/React.createElement("div", {
25032
- className: css(styles$n.row)
25029
+ className: css(styles$p.row)
25033
25030
  }, /*#__PURE__*/React.createElement("div", {
25034
- className: css(styles$n.columnLeft)
25031
+ className: css(styles$p.columnLeft)
25035
25032
  }, /*#__PURE__*/React.createElement("div", {
25036
- className: css(sticky && styles$n.sticky)
25033
+ className: css(sticky && styles$p.sticky)
25037
25034
  }, /*#__PURE__*/React.createElement(HintEditor, _extends({}, data, {
25038
- className: css(styles$n.hintEditor),
25035
+ className: css(styles$p.hintEditor),
25039
25036
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
25040
25037
  apiOptions: apiOptions,
25041
25038
  showTitle: false,
@@ -25050,9 +25047,9 @@ const HintNodeContent = withStickiness(props => {
25050
25047
  isFirst: true,
25051
25048
  isLast: true
25052
25049
  })))), /*#__PURE__*/React.createElement("div", {
25053
- className: css(styles$n.columnRight)
25050
+ className: css(styles$p.columnRight)
25054
25051
  }, /*#__PURE__*/React.createElement("div", {
25055
- className: css(sticky && styles$n.sticky)
25052
+ className: css(sticky && styles$p.sticky)
25056
25053
  }, preview)));
25057
25054
  });
25058
25055
  const TagsNodeContent = props => {
@@ -25069,9 +25066,9 @@ const TagsNodeContent = props => {
25069
25066
  return null;
25070
25067
  }
25071
25068
  return /*#__PURE__*/React.createElement("div", {
25072
- className: css(styles$n.columnLeft)
25069
+ className: css(styles$p.columnLeft)
25073
25070
  }, /*#__PURE__*/React.createElement("div", {
25074
- className: css(styles$n.tagsEditor)
25071
+ className: css(styles$p.tagsEditor)
25075
25072
  }, /*#__PURE__*/React.createElement(GroupMetadataEditor, {
25076
25073
  value: data,
25077
25074
  onChange: newVal => actions.setValueAtPath(path, newVal),
@@ -25102,23 +25099,23 @@ const ArrayNodeContent = props => {
25102
25099
  const subpath = path.concat(i);
25103
25100
  const controls = [i > 0 && /*#__PURE__*/React.createElement("div", {
25104
25101
  key: "moveArrayElementUp",
25105
- className: css(styles$n.control)
25102
+ className: css(styles$p.control)
25106
25103
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25107
25104
  color: "orange",
25108
25105
  title: "Move up",
25109
25106
  onClick: () => actions.moveArrayElementUp(subpath)
25110
25107
  }, /*#__PURE__*/React.createElement("div", {
25111
- className: css(styles$n.verticalFlip)
25108
+ className: css(styles$p.verticalFlip)
25112
25109
  }, /*#__PURE__*/React.createElement(InlineIcon$6, iconChevronDown)))), i < data.length - 1 && /*#__PURE__*/React.createElement("div", {
25113
25110
  key: "moveArrayElementDown",
25114
- className: css(styles$n.control)
25111
+ className: css(styles$p.control)
25115
25112
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25116
25113
  color: "orange",
25117
25114
  title: "Move down",
25118
25115
  onClick: () => actions.moveArrayElementDown(subpath)
25119
25116
  }, /*#__PURE__*/React.createElement(InlineIcon$6, iconChevronDown))), /*#__PURE__*/React.createElement("div", {
25120
25117
  key: "removeArrayElement",
25121
- className: css(styles$n.control)
25118
+ className: css(styles$p.control)
25122
25119
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25123
25120
  color: "orange",
25124
25121
  title: "Delete",
@@ -25126,7 +25123,7 @@ const ArrayNodeContent = props => {
25126
25123
  }, /*#__PURE__*/React.createElement(InlineIcon$6, iconTrash$4)))];
25127
25124
  return /*#__PURE__*/React.createElement("div", {
25128
25125
  key: i,
25129
- className: css(styles$n.arrayElement, !elementIsLeaf && styles$n.arrayElementAndNotLeaf)
25126
+ className: css(styles$p.arrayElement, !elementIsLeaf && styles$p.arrayElementAndNotLeaf)
25130
25127
  }, /*#__PURE__*/React.createElement(NodeContainer, _extends({}, otherProps, {
25131
25128
  key: i,
25132
25129
  shape: shape.elementShape,
@@ -25152,7 +25149,7 @@ const ObjectNodeContent = props => {
25152
25149
  // made sense to the shape's author is the order in which we'll iterate :)
25153
25150
  const children = Object.keys(shape.shape).map(subkey => /*#__PURE__*/React.createElement("div", {
25154
25151
  key: subkey,
25155
- className: css(styles$n.objectElement)
25152
+ className: css(styles$p.objectElement)
25156
25153
  }, /*#__PURE__*/React.createElement(NodeContainer, _extends({}, otherProps, {
25157
25154
  shape: shape.shape[subkey],
25158
25155
  data: data[subkey],
@@ -25319,7 +25316,7 @@ class MultiRendererEditor extends React.Component {
25319
25316
  }, this._renderContent());
25320
25317
  }
25321
25318
  }
25322
- const styles$n = StyleSheet.create({
25319
+ const styles$p = StyleSheet.create({
25323
25320
  // This is used in a number of places throughout this file.
25324
25321
  container: {},
25325
25322
  // eslint-disable-next-line react-native/no-unused-styles
@@ -25471,47 +25468,47 @@ const StatefulArticleEditor = createReactClass({
25471
25468
  screen
25472
25469
  } = this.state;
25473
25470
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
25474
- style: styles$m.controlBar
25471
+ style: styles$o.controlBar
25475
25472
  }, /*#__PURE__*/React.createElement("span", {
25476
- style: styles$m.controls
25473
+ style: styles$o.controls
25477
25474
  }, "Mode:", " ", /*#__PURE__*/React.createElement("span", {
25478
25475
  onClick: () => this.setState({
25479
25476
  mode: "edit"
25480
25477
  }),
25481
- style: mode === "edit" ? styles$m.controlSelected : styles$m.control
25478
+ style: mode === "edit" ? styles$o.controlSelected : styles$o.control
25482
25479
  }, "EDIT"), " | ", /*#__PURE__*/React.createElement("span", {
25483
25480
  onClick: () => this.setState({
25484
25481
  mode: "preview"
25485
25482
  }),
25486
- style: mode === "preview" ? styles$m.controlSelected : styles$m.control
25483
+ style: mode === "preview" ? styles$o.controlSelected : styles$o.control
25487
25484
  }, "PREVIEW"), " | ", /*#__PURE__*/React.createElement("span", {
25488
25485
  onClick: () => this.setState({
25489
25486
  mode: "json"
25490
25487
  }),
25491
- style: mode === "json" ? styles$m.controlSelected : styles$m.control
25488
+ style: mode === "json" ? styles$o.controlSelected : styles$o.control
25492
25489
  }, "JSON")), /*#__PURE__*/React.createElement("span", {
25493
- style: styles$m.controls
25490
+ style: styles$o.controls
25494
25491
  }, "Screen:", " ", /*#__PURE__*/React.createElement("span", {
25495
25492
  onClick: () => this.setState({
25496
25493
  screen: "phone"
25497
25494
  }),
25498
- style: screen === "phone" ? styles$m.controlSelected : styles$m.control
25495
+ style: screen === "phone" ? styles$o.controlSelected : styles$o.control
25499
25496
  }, "PHONE"), " | ", /*#__PURE__*/React.createElement("span", {
25500
25497
  onClick: () => this.setState({
25501
25498
  screen: "tablet"
25502
25499
  }),
25503
- style: screen === "tablet" ? styles$m.controlSelected : styles$m.control
25500
+ style: screen === "tablet" ? styles$o.controlSelected : styles$o.control
25504
25501
  }, "TABLET"), " | ", /*#__PURE__*/React.createElement("span", {
25505
25502
  onClick: () => this.setState({
25506
25503
  screen: "desktop"
25507
25504
  }),
25508
- style: screen === "desktop" ? styles$m.controlSelected : styles$m.control
25505
+ style: screen === "desktop" ? styles$o.controlSelected : styles$o.control
25509
25506
  }, "DESKTOP"))), /*#__PURE__*/React.createElement("div", {
25510
- style: styles$m.editor
25507
+ style: styles$o.editor
25511
25508
  }, /*#__PURE__*/React.createElement(ArticleEditor, this.state)));
25512
25509
  }
25513
25510
  });
25514
- const styles$m = {
25511
+ const styles$o = {
25515
25512
  controlBar: {
25516
25513
  marginBottom: 25,
25517
25514
  marginTop: 5
@@ -27001,10 +26998,10 @@ class GradedGroupEditor extends React.Component {
27001
26998
  }, /*#__PURE__*/React.createElement("div", {
27002
26999
  className: "perseus-widget-row"
27003
27000
  }, /*#__PURE__*/React.createElement("label", {
27004
- className: css(styles$l.title)
27001
+ className: css(styles$n.title)
27005
27002
  }, "Title:", " ", /*#__PURE__*/React.createElement(TextInput$6, {
27006
27003
  value: this.props.title,
27007
- className: css(styles$l.input)
27004
+ className: css(styles$n.input)
27008
27005
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
27009
27006
  ,
27010
27007
  onChange: this.change("title")
@@ -27029,7 +27026,7 @@ class GradedGroupEditor extends React.Component {
27029
27026
  }, /*#__PURE__*/React.createElement(InlineIcon$4, iconPlus$1), " Add a hint"), this.props.hint && /*#__PURE__*/React.createElement("div", {
27030
27027
  className: "perseus-hint-editor"
27031
27028
  }, /*#__PURE__*/React.createElement("div", {
27032
- className: css(styles$l.hintsTitle)
27029
+ className: css(styles$n.hintsTitle)
27033
27030
  }, "Hint"), /*#__PURE__*/React.createElement(Editor, {
27034
27031
  ref: this.hintEditor,
27035
27032
  content: this.props.hint ? this.props.hint.content : "",
@@ -27066,7 +27063,7 @@ GradedGroupEditor.defaultProps = {
27066
27063
  images: {},
27067
27064
  hint: null
27068
27065
  };
27069
- const styles$l = StyleSheet.create({
27066
+ const styles$n = StyleSheet.create({
27070
27067
  title: {
27071
27068
  fontSize: 18,
27072
27069
  fontWeight: "bold"
@@ -28244,7 +28241,7 @@ class InputNumberEditor extends React.Component {
28244
28241
  super(...args);
28245
28242
  this.input = /*#__PURE__*/React.createRef();
28246
28243
  this.handleAnswerChange = str => {
28247
- const value = Util.firstNumericalParse(str) || 0;
28244
+ const value = Util.firstNumericalParse(str, this.context.strings) || 0;
28248
28245
  this.props.onChange({
28249
28246
  value: value
28250
28247
  });
@@ -28310,7 +28307,7 @@ class InputNumberEditor extends React.Component {
28310
28307
  defaultValue: this.props.maxError,
28311
28308
  "aria-label": "Max error",
28312
28309
  onBlur: e => {
28313
- const ans = "" + (Util.firstNumericalParse(e.target.value) || 0);
28310
+ const ans = "" + (Util.firstNumericalParse(e.target.value, this.context.strings) || 0);
28314
28311
  e.target.value = ans;
28315
28312
  this.props.onChange({
28316
28313
  maxError: ans
@@ -28348,6 +28345,7 @@ class InputNumberEditor extends React.Component {
28348
28345
  }), " ", "Right alignment")));
28349
28346
  }
28350
28347
  }
28348
+ InputNumberEditor.contextType = PerseusI18nContext;
28351
28349
  InputNumberEditor.widgetName = "input-number";
28352
28350
  InputNumberEditor.defaultProps = {
28353
28351
  value: 0,
@@ -28367,12 +28365,15 @@ InputNumberEditor.defaultProps = {
28367
28365
  function MathquillInput(props) {
28368
28366
  const mathFieldWrapperRef = useRef(null);
28369
28367
  const mathFieldInstance = useRef();
28368
+ const {
28369
+ strings
28370
+ } = useMathInputI18n();
28370
28371
  useEffect(() => {
28371
28372
  // If we have the mount for the input, but not the input
28372
28373
  // make a new Mathquill input
28373
28374
  if (mathFieldWrapperRef.current && !mathFieldInstance.current) {
28374
28375
  // Initialize MathQuill.MathField instance
28375
- mathFieldInstance.current = createMathField(mathFieldWrapperRef.current, baseConfig => _extends({}, baseConfig, {
28376
+ mathFieldInstance.current = createMathField(mathFieldWrapperRef.current, strings, baseConfig => _extends({}, baseConfig, {
28376
28377
  handlers: {
28377
28378
  edit: mathField => {
28378
28379
  // This handler is guaranteed to be called on change, but
@@ -28400,13 +28401,13 @@ function MathquillInput(props) {
28400
28401
  }
28401
28402
  });
28402
28403
  return /*#__PURE__*/React.createElement(View, {
28403
- style: styles$k.outerWrapper
28404
+ style: styles$m.outerWrapper
28404
28405
  }, /*#__PURE__*/React.createElement("span", {
28405
28406
  ref: mathFieldWrapperRef,
28406
28407
  className: "perseus-math-input mq-editable-field mq-math-mode"
28407
28408
  }));
28408
28409
  }
28409
- const styles$k = StyleSheet.create({
28410
+ const styles$m = StyleSheet.create({
28410
28411
  outerWrapper: {
28411
28412
  display: "inline-block",
28412
28413
  borderStyle: "solid",
@@ -29467,7 +29468,7 @@ const GraphPointsCountSelector = ({
29467
29468
  // Never uses placeholder, always has value
29468
29469
  ,
29469
29470
  placeholder: "",
29470
- style: styles$j.singleSelectShort
29471
+ style: styles$l.singleSelectShort
29471
29472
  }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React.createElement(OptionItem, {
29472
29473
  key: n,
29473
29474
  value: `${n}`,
@@ -29478,7 +29479,7 @@ const GraphPointsCountSelector = ({
29478
29479
  label: "unlimited"
29479
29480
  })]);
29480
29481
  };
29481
- const styles$j = StyleSheet.create({
29482
+ const styles$l = StyleSheet.create({
29482
29483
  singleSelectShort: {
29483
29484
  // Non-standard spacing, but it's the smallest we can go
29484
29485
  // without running into styling issues with the dropdown.
@@ -29491,7 +29492,7 @@ const GraphTypeSelector = props => {
29491
29492
  selectedValue: props.graphType,
29492
29493
  onChange: props.onChange,
29493
29494
  placeholder: "Select a graph type",
29494
- style: styles$i.singleSelectShort
29495
+ style: styles$k.singleSelectShort
29495
29496
  }, /*#__PURE__*/React.createElement(OptionItem, {
29496
29497
  value: "linear",
29497
29498
  label: "Linear function"
@@ -29524,7 +29525,7 @@ const GraphTypeSelector = props => {
29524
29525
  label: "Angle"
29525
29526
  }));
29526
29527
  };
29527
- const styles$i = StyleSheet.create({
29528
+ const styles$k = StyleSheet.create({
29528
29529
  singleSelectShort: {
29529
29530
  // Non-standard spacing, but it's the smallest we can go
29530
29531
  // without running into styling issues with the dropdown.
@@ -29541,16 +29542,16 @@ const LabeledRow = props => {
29541
29542
  style
29542
29543
  } = props;
29543
29544
  return /*#__PURE__*/React.createElement("label", {
29544
- className: css(styles$h.label)
29545
+ className: css(styles$j.label)
29545
29546
  }, /*#__PURE__*/React.createElement(View, {
29546
- style: [styles$h.row, style]
29547
+ style: [styles$j.row, style]
29547
29548
  }, labelSide === "start" || /*#__PURE__*/React.createElement(LabelSmall, {
29548
- style: styles$h.spaceEnd
29549
+ style: styles$j.spaceEnd
29549
29550
  }, label), children, labelSide === "end" && /*#__PURE__*/React.createElement(LabelSmall, {
29550
- style: styles$h.spaceStart
29551
+ style: styles$j.spaceStart
29551
29552
  }, label)));
29552
29553
  };
29553
- const styles$h = StyleSheet.create({
29554
+ const styles$j = StyleSheet.create({
29554
29555
  label: {
29555
29556
  width: "fit-content"
29556
29557
  },
@@ -29944,10 +29945,10 @@ class InteractiveGraphSettings extends React.Component {
29944
29945
  onChange: this.change
29945
29946
  }))), /*#__PURE__*/React.createElement(LabeledRow, {
29946
29947
  label: "Background image URL:",
29947
- style: styles$g.resetSpaceTop
29948
+ style: styles$i.resetSpaceTop
29948
29949
  }, /*#__PURE__*/React.createElement("input", {
29949
29950
  type: "text",
29950
- className: css(styles$g.backgroundUrlInput),
29951
+ className: css(styles$i.backgroundUrlInput),
29951
29952
  ref: this.bgUrlRef,
29952
29953
  value: this.state.backgroundImage.url || "",
29953
29954
  onChange: e => {
@@ -29961,24 +29962,24 @@ class InteractiveGraphSettings extends React.Component {
29961
29962
  onKeyPress: this.changeBackgroundUrl,
29962
29963
  onBlur: this.changeBackgroundUrl
29963
29964
  }), /*#__PURE__*/React.createElement(InfoTip$c, null, /*#__PURE__*/React.createElement("p", null, "Create an image in graphie, or use the \"Add image\" function to create a background."))), /*#__PURE__*/React.createElement(View, {
29964
- style: styles$g.rulerSection
29965
+ style: styles$i.rulerSection
29965
29966
  }, /*#__PURE__*/React.createElement(View, {
29966
- style: styles$g.checkboxRow
29967
+ style: styles$i.checkboxRow
29967
29968
  }, /*#__PURE__*/React.createElement(PropCheckBox$7, {
29968
29969
  label: "Show ruler",
29969
29970
  showRuler: this.props.showRuler,
29970
29971
  onChange: this.change,
29971
- style: styles$g.resetSpaceTop
29972
+ style: styles$i.resetSpaceTop
29972
29973
  }), /*#__PURE__*/React.createElement(PropCheckBox$7, {
29973
29974
  label: "Show protractor",
29974
29975
  showProtractor: this.props.showProtractor,
29975
29976
  onChange: this.change,
29976
- style: styles$g.resetSpaceTop
29977
+ style: styles$i.resetSpaceTop
29977
29978
  })), this.props.showRuler && /*#__PURE__*/React.createElement(View, {
29978
- style: styles$g.spaceTop
29979
+ style: styles$i.spaceTop
29979
29980
  }, /*#__PURE__*/React.createElement(LabeledRow, {
29980
29981
  label: "Ruler label:",
29981
- style: styles$g.resetSpaceTop
29982
+ style: styles$i.resetSpaceTop
29982
29983
  }, /*#__PURE__*/React.createElement(SingleSelect, {
29983
29984
  id: "ruler-label-select",
29984
29985
  selectedValue: this.props.rulerLabel,
@@ -29988,7 +29989,7 @@ class InteractiveGraphSettings extends React.Component {
29988
29989
  });
29989
29990
  },
29990
29991
  placeholder: "None",
29991
- style: styles$g.singleSelectShort
29992
+ style: styles$i.singleSelectShort
29992
29993
  }, /*#__PURE__*/React.createElement(OptionItem, {
29993
29994
  value: "",
29994
29995
  label: "None",
@@ -30029,7 +30030,7 @@ class InteractiveGraphSettings extends React.Component {
30029
30030
  });
30030
30031
  },
30031
30032
  placeholder: "10",
30032
- style: styles$g.singleSelectShort
30033
+ style: styles$i.singleSelectShort
30033
30034
  }, [1, 2, 4, 8, 10, 16].map(value => /*#__PURE__*/React.createElement(OptionItem, {
30034
30035
  key: value,
30035
30036
  value: `${value}`,
@@ -30053,7 +30054,7 @@ InteractiveGraphSettings.defaultProps = {
30053
30054
  rulerLabel: "",
30054
30055
  rulerTicks: 10
30055
30056
  };
30056
- const styles$g = StyleSheet.create({
30057
+ const styles$i = StyleSheet.create({
30057
30058
  resetSpaceTop: {
30058
30059
  marginTop: 0
30059
30060
  },
@@ -30097,22 +30098,26 @@ const LockedFigureSelect = props => {
30097
30098
  onChange
30098
30099
  } = props;
30099
30100
  return /*#__PURE__*/React.createElement(View, {
30100
- style: styles$f.container
30101
+ style: styles$h.container
30101
30102
  }, /*#__PURE__*/React.createElement(ActionMenu, {
30102
- menuText: "Add element",
30103
- style: styles$f.addElementSelect
30103
+ menuText: "Add locked figure",
30104
+ style: styles$h.addElementSelect
30104
30105
  }, [/*#__PURE__*/React.createElement(ActionItem, {
30105
30106
  key: `${id}-point`,
30106
30107
  label: "Point",
30107
30108
  onClick: () => onChange("point")
30108
- }, "Point")]));
30109
+ }, "Point"), /*#__PURE__*/React.createElement(ActionItem, {
30110
+ key: `${id}-line`,
30111
+ label: "Line",
30112
+ onClick: () => onChange("line")
30113
+ }, "Line")]));
30109
30114
  };
30110
- const styles$f = StyleSheet.create({
30115
+ const styles$h = StyleSheet.create({
30111
30116
  container: {
30112
- marginTop: spacing.medium_16
30117
+ marginTop: spacing.xxxSmall_4
30113
30118
  },
30114
30119
  addElementSelect: {
30115
- backgroundColor: color.fadedBlue16,
30120
+ backgroundColor: color.fadedBlue8,
30116
30121
  borderRadius: spacing.xxxSmall_4
30117
30122
  }
30118
30123
  });
@@ -30168,21 +30173,99 @@ const defaultContext = {
30168
30173
  };
30169
30174
  const MediaLayoutContext = React.createContext(defaultContext);
30170
30175
  MediaLayoutContext.displayName = "MediaLayoutContext";
30176
+ StyleSheet.create({
30177
+ grow: {
30178
+ flexGrow: 1
30179
+ }
30180
+ });
30171
30181
 
30172
- class Spring extends React.Component {
30182
+ class Strut extends React.Component {
30173
30183
  render() {
30174
30184
  const {
30185
+ size,
30175
30186
  style
30176
30187
  } = this.props;
30177
30188
  return React.createElement(View, {
30178
30189
  "aria-hidden": "true",
30179
- style: [styles$e.grow, style]
30190
+ style: [strutStyle(size), style]
30180
30191
  });
30181
30192
  }
30182
30193
  }
30183
- const styles$e = StyleSheet.create({
30184
- grow: {
30185
- flexGrow: 1
30194
+ const strutStyle = size => {
30195
+ return {
30196
+ width: size,
30197
+ MsFlexBasis: size,
30198
+ MsFlexPreferredSize: size,
30199
+ WebkitFlexBasis: size,
30200
+ flexBasis: size,
30201
+ flexShrink: 0
30202
+ };
30203
+ };
30204
+
30205
+ const ColorSwatch = props => {
30206
+ const {
30207
+ color: color$1,
30208
+ filled = true,
30209
+ decorative = false
30210
+ } = props;
30211
+ return /*#__PURE__*/React.createElement(View, {
30212
+ "aria-label":
30213
+ // Don't include an aria label for decorative circles.
30214
+ !decorative ? `Color: ${color$1}, ${filled ? "filled" : "open"}` : undefined,
30215
+ style: [styles$g.colorSwatch, {
30216
+ border: `4px solid ${lockedFigureColors[color$1]}`,
30217
+ backgroundColor: filled ? lockedFigureColors[color$1] : color.white
30218
+ }]
30219
+ });
30220
+ };
30221
+ const styles$g = StyleSheet.create({
30222
+ colorSwatch: {
30223
+ // Add a white outline so that the color swatch is visible when
30224
+ // the dropdown option is highlighted with its blue background.
30225
+ outline: `2px solid ${color.offWhite}`,
30226
+ borderRadius: "50%",
30227
+ width: spacing.large_24,
30228
+ height: spacing.large_24
30229
+ }
30230
+ });
30231
+
30232
+ const possibleColors = Object.keys(lockedFigureColors);
30233
+ const ColorSelect = props => {
30234
+ const {
30235
+ id,
30236
+ selectedValue,
30237
+ onChange
30238
+ } = props;
30239
+ return /*#__PURE__*/React.createElement(View, {
30240
+ style: styles$f.row
30241
+ }, /*#__PURE__*/React.createElement(LabelMedium, {
30242
+ htmlFor: id,
30243
+ style: styles$f.label,
30244
+ tag: "label"
30245
+ }, "Color"), /*#__PURE__*/React.createElement(SingleSelect, {
30246
+ id: id,
30247
+ selectedValue: selectedValue,
30248
+ onChange: onChange
30249
+ // Placeholder is required, but never gets used.
30250
+ ,
30251
+ placeholder: ""
30252
+ }, possibleColors.map(colorName => /*#__PURE__*/React.createElement(OptionItem, {
30253
+ key: colorName,
30254
+ value: colorName,
30255
+ label: colorName,
30256
+ leftAccessory: /*#__PURE__*/React.createElement(ColorSwatch, {
30257
+ color: colorName,
30258
+ decorative: true
30259
+ })
30260
+ }, colorName))));
30261
+ };
30262
+ const styles$f = StyleSheet.create({
30263
+ row: {
30264
+ flexDirection: "row",
30265
+ alignItems: "center"
30266
+ },
30267
+ label: {
30268
+ marginInlineEnd: spacing.xSmall_8
30186
30269
  }
30187
30270
  });
30188
30271
 
@@ -30227,17 +30310,34 @@ function focusWithChromeStickyFocusBugWorkaround(element) {
30227
30310
  });
30228
30311
  }
30229
30312
  function getValidNumberFromString(value) {
30230
- const parsed = parseInt(value);
30313
+ const parsed = parseFloat(value);
30231
30314
  // If the value is not a number, return 0.
30232
30315
  return isNaN(parsed) ? 0 : parsed;
30233
30316
  }
30234
- function getDefaultFigureForFigureType(type) {
30317
+ function getDefaultFigureForType(type) {
30235
30318
  switch (type) {
30236
30319
  case "point":
30237
30320
  return {
30238
30321
  type: "point",
30239
- coord: [0, 0]
30322
+ coord: [0, 0],
30323
+ color: "blue",
30324
+ filled: true
30240
30325
  };
30326
+ case "line":
30327
+ return {
30328
+ type: "line",
30329
+ kind: "line",
30330
+ points: [getDefaultFigureForType("point"), _extends({}, getDefaultFigureForType("point"), {
30331
+ coord: [2, 2]
30332
+ })],
30333
+ color: "blue",
30334
+ lineStyle: "solid",
30335
+ showArrows: false,
30336
+ showStartPoint: false,
30337
+ showEndPoint: false
30338
+ };
30339
+ default:
30340
+ throw new UnreachableCaseError(type);
30241
30341
  }
30242
30342
  }
30243
30343
 
@@ -30247,15 +30347,21 @@ function getDefaultFigureForFigureType(type) {
30247
30347
  *
30248
30348
  * Used in the interactive graph editor's locked figures section.
30249
30349
  */
30250
- const possibleColors = ["purple", "blue", "teal", "green", "gold", "red", "pink"];
30251
30350
  const LockedPointSettings = props => {
30252
30351
  const {
30253
30352
  coord,
30353
+ color: pointColor,
30354
+ filled = true,
30355
+ label,
30356
+ toggled = "true",
30254
30357
  style,
30255
- onChangeColor,
30256
- onChangeCoord,
30358
+ onChangeProps,
30359
+ onToggle,
30257
30360
  onRemove
30258
30361
  } = props;
30362
+
30363
+ // Keep track of the coordinates via state as the user is editing them,
30364
+ // before they are updated in the props on blur.
30259
30365
  const [coordState, setCoordState] = React.useState([
30260
30366
  // Using strings to make it easier to work with the text fields.
30261
30367
  coord[0].toString(), coord[1].toString()]);
@@ -30265,101 +30371,330 @@ const LockedPointSettings = props => {
30265
30371
  const ids = useUniqueIdWithMock();
30266
30372
  const xCoordId = ids.get("x-coord");
30267
30373
  const yCoordId = ids.get("y-coord");
30268
- const colorSelectId = ids.get("color-select");
30374
+ const colorSelectId = ids.get("point-color-select");
30375
+ const showPointToggleId = ids.get("show-point-toggle");
30269
30376
  function handleBlur() {
30270
30377
  const validCoord = [getValidNumberFromString(coordState[0]), getValidNumberFromString(coordState[1])];
30271
30378
 
30272
30379
  // Make the text field only show valid numbers after blur.
30273
30380
  setCoordState([validCoord[0].toString(), validCoord[1].toString()]);
30274
30381
  // Update the graph with the new coordinates.
30275
- onChangeCoord(validCoord);
30382
+ onChangeProps({
30383
+ coord: validCoord
30384
+ });
30276
30385
  }
30277
30386
  function handleCoordChange(newValue, coordIndex) {
30278
30387
  const newCoord = [...coordState];
30279
30388
  newCoord[coordIndex] = newValue;
30280
30389
  setCoordState(newCoord);
30281
30390
  }
30282
- return /*#__PURE__*/React.createElement(View, {
30283
- style: styles$d.container
30391
+ function handleColorChange(newValue) {
30392
+ onChangeProps({
30393
+ color: newValue
30394
+ });
30395
+ }
30396
+ return /*#__PURE__*/React.createElement(View
30397
+ // More specificity so that we can override the default
30398
+ // .heading > h2 > .header styles from the articles.less
30399
+ // file (which is imported in perseus-renderer.less).
30400
+ , {
30401
+ className: "locked-figure-accordion"
30402
+ }, /*#__PURE__*/React.createElement(AccordionSection, {
30403
+ style: [styles$e.container, style],
30404
+ headerStyle: styles$e.accordionHeader,
30405
+ header:
30406
+ /*#__PURE__*/
30407
+ // Summary: Point, coords, color (filled/open)
30408
+ React.createElement(View, {
30409
+ style: styles$e.row
30410
+ }, /*#__PURE__*/React.createElement(LabelLarge, null, `${label || "Point"} (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React.createElement(Strut, {
30411
+ size: spacing.xSmall_8
30412
+ }), toggled && /*#__PURE__*/React.createElement(ColorSwatch, {
30413
+ color: pointColor,
30414
+ filled: filled
30415
+ }))
30284
30416
  }, /*#__PURE__*/React.createElement(View, {
30285
- style: styles$d.row
30286
- }, /*#__PURE__*/React.createElement(LabelLarge, null, "Point"), /*#__PURE__*/React.createElement(Spring, null), /*#__PURE__*/React.createElement(IconButton, {
30287
- icon: trashIcon,
30288
- "aria-label": `Delete locked point at ${coordState[0]}, ${coordState[1]}`,
30289
- onClick: onRemove
30290
- })), /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(View, {
30291
- style: styles$d.row
30417
+ style: styles$e.accordionPanel
30418
+ }, /*#__PURE__*/React.createElement(View, {
30419
+ style: styles$e.row
30420
+ }, /*#__PURE__*/React.createElement(View, {
30421
+ style: [styles$e.row, styles$e.spaceUnder]
30292
30422
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30293
30423
  htmlFor: xCoordId,
30294
- style: styles$d.label,
30424
+ style: styles$e.label,
30295
30425
  tag: "label"
30296
- }, "x Coordinate"), /*#__PURE__*/React.createElement(TextField, {
30426
+ }, "x Coord"), /*#__PURE__*/React.createElement(TextField, {
30297
30427
  id: xCoordId,
30428
+ type: "number",
30298
30429
  value: coordState[0],
30299
30430
  onChange: newValue => handleCoordChange(newValue, 0),
30300
30431
  onBlur: handleBlur,
30301
- style: styles$d.textField
30302
- })), /*#__PURE__*/React.createElement(View, {
30303
- style: styles$d.row
30432
+ style: styles$e.textField
30433
+ })), /*#__PURE__*/React.createElement(Strut, {
30434
+ size: spacing.medium_16
30435
+ }), /*#__PURE__*/React.createElement(View, {
30436
+ style: [styles$e.row, styles$e.spaceUnder]
30304
30437
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30305
30438
  htmlFor: yCoordId,
30306
- style: styles$d.label,
30439
+ style: styles$e.label,
30307
30440
  tag: "label"
30308
- }, "y Coordinate"), /*#__PURE__*/React.createElement(TextField, {
30441
+ }, "y Coord"), /*#__PURE__*/React.createElement(TextField, {
30309
30442
  id: yCoordId,
30443
+ type: "number",
30310
30444
  value: coordState[1],
30311
30445
  onChange: newValue => handleCoordChange(newValue, 1),
30312
30446
  onBlur: handleBlur,
30313
- style: styles$d.textField
30447
+ style: styles$e.textField
30448
+ }))), onToggle && /*#__PURE__*/React.createElement(View, {
30449
+ style: [styles$e.row, styles$e.spaceUnder]
30450
+ }, /*#__PURE__*/React.createElement(Switch, {
30451
+ id: showPointToggleId,
30452
+ checked: !!toggled,
30453
+ onChange: onToggle
30454
+ }), /*#__PURE__*/React.createElement(Strut, {
30455
+ size: spacing.small_12
30456
+ }), /*#__PURE__*/React.createElement(LabelMedium, {
30457
+ tag: "label",
30458
+ htmlFor: showPointToggleId
30459
+ }, "Show point on graph")), toggled && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColorSelect, {
30460
+ id: colorSelectId,
30461
+ selectedValue: pointColor || "blue",
30462
+ onChange: handleColorChange
30463
+ }), /*#__PURE__*/React.createElement(Strut, {
30464
+ size: spacing.xSmall_8
30465
+ }), /*#__PURE__*/React.createElement(Checkbox$1, {
30466
+ label: "Open point",
30467
+ checked: !filled,
30468
+ onChange: newValue => {
30469
+ onChangeProps({
30470
+ filled: !newValue
30471
+ });
30472
+ },
30473
+ style: styles$e.spaceUnder
30474
+ })), onRemove && /*#__PURE__*/React.createElement(IconButton, {
30475
+ icon: trashIcon,
30476
+ "aria-label": `Delete locked point at ${coordState[0]}, ${coordState[1]}`,
30477
+ onClick: onRemove,
30478
+ style: styles$e.deleteButton
30479
+ }))));
30480
+ };
30481
+ const styles$e = StyleSheet.create({
30482
+ container: {
30483
+ backgroundColor: color.fadedBlue8,
30484
+ marginBottom: spacing.small_12
30485
+ },
30486
+ accordionHeader: {
30487
+ paddingTop: spacing.small_12,
30488
+ paddingBottom: spacing.small_12,
30489
+ paddingInlineStart: spacing.medium_16,
30490
+ // Fixed height so the addition of the color swatch doesn't
30491
+ // change the height of the header when toggling.
30492
+ height: spacing.xxLarge_48
30493
+ },
30494
+ accordionPanel: {
30495
+ padding: spacing.medium_16,
30496
+ paddingBottom: spacing.xSmall_8
30497
+ },
30498
+ row: {
30499
+ flexDirection: "row",
30500
+ alignItems: "center"
30501
+ },
30502
+ spaceUnder: {
30503
+ marginBottom: spacing.xSmall_8
30504
+ },
30505
+ label: {
30506
+ marginInlineEnd: spacing.xSmall_8
30507
+ },
30508
+ textField: {
30509
+ width: spacing.xxxLarge_64
30510
+ },
30511
+ deleteButton: {
30512
+ position: "absolute",
30513
+ top: spacing.small_12,
30514
+ right: spacing.small_12
30515
+ }
30516
+ });
30517
+
30518
+ const LockedLineSettings = props => {
30519
+ const {
30520
+ kind,
30521
+ points,
30522
+ color: lineColor = "blue",
30523
+ lineStyle = "solid",
30524
+ showArrows,
30525
+ showStartPoint,
30526
+ showEndPoint,
30527
+ onChangeProps,
30528
+ onRemove
30529
+ } = props;
30530
+ const [startPoint, endPoint] = points;
30531
+
30532
+ // Generate unique IDs so that the programmatic labels can be associated
30533
+ // with their respective text fields.
30534
+ const ids = useUniqueIdWithMock();
30535
+ const kindSelectId = ids.get("line-kind-select");
30536
+ const colorSelectId = ids.get("line-color-select");
30537
+ const styleSelectId = ids.get("line-style-select");
30538
+ const lineLabel = `Line (${startPoint.coord[0]}, ${startPoint.coord[1]}),
30539
+ (${endPoint.coord[0]}, ${endPoint.coord[1]})`;
30540
+ function handleChangePoint(newPointProps, index) {
30541
+ const newPoints = [...points];
30542
+ newPoints[index] = _extends({}, points[index], newPointProps);
30543
+ onChangeProps({
30544
+ points: newPoints
30545
+ });
30546
+ }
30547
+ function handleColorChange(newColor) {
30548
+ onChangeProps({
30549
+ color: newColor,
30550
+ // Keep the line's points' colors in sync with the line color.
30551
+ points: [_extends({}, startPoint, {
30552
+ color: newColor
30553
+ }), _extends({}, endPoint, {
30554
+ color: newColor
30555
+ })]
30556
+ });
30557
+ }
30558
+ return /*#__PURE__*/React.createElement(View
30559
+ // More specificity so that we can override the default
30560
+ // .heading > h2 > .header styles from the articles.less
30561
+ // file (which is imported in perseus-renderer.less).
30562
+ , {
30563
+ className: "locked-figure-accordion"
30564
+ }, /*#__PURE__*/React.createElement(AccordionSection, {
30565
+ style: styles$d.container,
30566
+ headerStyle: styles$d.accordionHeader,
30567
+ header: /*#__PURE__*/React.createElement(View, {
30568
+ style: styles$d.row
30569
+ }, /*#__PURE__*/React.createElement(LabelLarge, null, lineLabel), /*#__PURE__*/React.createElement(Strut, {
30570
+ size: spacing.xSmall_8
30571
+ }), /*#__PURE__*/React.createElement(ColorSwatch, {
30572
+ color: lineColor
30573
+ }))
30574
+ }, /*#__PURE__*/React.createElement(View, {
30575
+ style: styles$d.accordionPanel
30576
+ }, /*#__PURE__*/React.createElement(View, {
30577
+ style: [styles$d.row, styles$d.spaceUnder]
30578
+ }, /*#__PURE__*/React.createElement(LabelMedium, {
30579
+ htmlFor: kindSelectId,
30580
+ style: styles$d.label,
30581
+ tag: "label"
30582
+ }, "Kind"), /*#__PURE__*/React.createElement(SingleSelect, {
30583
+ id: kindSelectId,
30584
+ selectedValue: kind,
30585
+ onChange: value => onChangeProps({
30586
+ kind: value
30587
+ })
30588
+ // Placeholder is required, but never gets used.
30589
+ ,
30590
+ placeholder: ""
30591
+ }, /*#__PURE__*/React.createElement(OptionItem, {
30592
+ value: "line",
30593
+ label: "line"
30594
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30595
+ value: "segment",
30596
+ label: "segment"
30597
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30598
+ value: "ray",
30599
+ label: "ray"
30314
30600
  }))), /*#__PURE__*/React.createElement(View, {
30601
+ style: [styles$d.row, styles$d.spaceUnder]
30602
+ }, /*#__PURE__*/React.createElement(ColorSelect, {
30603
+ id: colorSelectId,
30604
+ selectedValue: lineColor || "blue",
30605
+ onChange: handleColorChange
30606
+ }), /*#__PURE__*/React.createElement(Strut, {
30607
+ size: spacing.medium_16
30608
+ }), /*#__PURE__*/React.createElement(View, {
30315
30609
  style: styles$d.row
30316
30610
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30317
- htmlFor: colorSelectId,
30611
+ htmlFor: styleSelectId,
30318
30612
  style: styles$d.label,
30319
30613
  tag: "label"
30320
- }, "Color"), /*#__PURE__*/React.createElement(SingleSelect, {
30321
- id: colorSelectId,
30322
- selectedValue: (style == null ? void 0 : style.fill) || "blue",
30323
- onChange: onChangeColor
30614
+ }, "Style"), /*#__PURE__*/React.createElement(SingleSelect, {
30615
+ id: styleSelectId,
30616
+ selectedValue: lineStyle,
30617
+ onChange: value => onChangeProps({
30618
+ lineStyle: value
30619
+ })
30324
30620
  // Placeholder is required, but never gets used.
30325
30621
  ,
30326
30622
  placeholder: ""
30327
- }, possibleColors.map(colorName => /*#__PURE__*/React.createElement(OptionItem, {
30328
- key: colorName,
30329
- value: colorName,
30330
- label: colorName,
30331
- leftAccessory: /*#__PURE__*/React.createElement(View, {
30332
- style: [styles$d.colorCircle, {
30333
- backgroundColor: color[colorName]
30334
- }]
30335
- })
30336
- }, colorName)))));
30623
+ }, /*#__PURE__*/React.createElement(OptionItem, {
30624
+ value: "solid",
30625
+ label: "solid"
30626
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30627
+ value: "dashed",
30628
+ label: "dashed"
30629
+ })))), /*#__PURE__*/React.createElement(Checkbox$1, {
30630
+ label: "Show arrows",
30631
+ checked: showArrows,
30632
+ onChange: newValue => onChangeProps({
30633
+ showArrows: newValue
30634
+ }),
30635
+ style: styles$d.spaceUnder
30636
+ }), /*#__PURE__*/React.createElement(LockedPointSettings, _extends({
30637
+ label: "Start point",
30638
+ toggled: showStartPoint
30639
+ }, startPoint, {
30640
+ onToggle: newValue => onChangeProps({
30641
+ showStartPoint: newValue
30642
+ }),
30643
+ onChangeProps: newProps => handleChangePoint(newProps, 0),
30644
+ style: styles$d.lockedPointSettingsContainer
30645
+ })), /*#__PURE__*/React.createElement(LockedPointSettings, _extends({
30646
+ label: "End point",
30647
+ toggled: showEndPoint
30648
+ }, endPoint, {
30649
+ onToggle: newValue => onChangeProps({
30650
+ showEndPoint: newValue
30651
+ }),
30652
+ onChangeProps: newProps => handleChangePoint(newProps, 1),
30653
+ style: styles$d.lockedPointSettingsContainer
30654
+ })), /*#__PURE__*/React.createElement(IconButton, {
30655
+ icon: trashIcon,
30656
+ "aria-label": `Delete locked line defined by
30657
+ ${startPoint.coord[0]}, ${startPoint.coord[1]} and
30658
+ ${endPoint.coord[0]}, ${endPoint.coord[1]}.`,
30659
+ onClick: onRemove,
30660
+ style: styles$d.deleteButton
30661
+ }))));
30337
30662
  };
30338
30663
  const styles$d = StyleSheet.create({
30339
30664
  container: {
30340
30665
  backgroundColor: color.fadedBlue8,
30341
- marginBottom: spacing.xSmall_8,
30666
+ marginBottom: spacing.xSmall_8
30667
+ },
30668
+ accordionHeader: {
30669
+ paddingTop: spacing.small_12,
30670
+ paddingBottom: spacing.small_12,
30671
+ paddingInlineStart: spacing.medium_16
30672
+ },
30673
+ accordionPanel: {
30342
30674
  padding: spacing.medium_16,
30343
- borderRadius: spacing.xSmall_8
30675
+ paddingBottom: spacing.xSmall_8
30676
+ },
30677
+ lockedPointSettingsContainer: {
30678
+ marginTop: spacing.xSmall_8,
30679
+ marginBottom: spacing.xSmall_8,
30680
+ marginLeft: -spacing.xxxSmall_4,
30681
+ marginRight: -spacing.xxxSmall_4,
30682
+ backgroundColor: color.white
30344
30683
  },
30345
30684
  row: {
30346
30685
  flexDirection: "row",
30347
- alignItems: "center",
30686
+ alignItems: "center"
30687
+ },
30688
+ spaceUnder: {
30348
30689
  marginBottom: spacing.xSmall_8
30349
30690
  },
30350
30691
  label: {
30351
30692
  marginInlineEnd: spacing.xSmall_8
30352
30693
  },
30353
- textField: {
30354
- width: spacing.xxxLarge_64
30355
- },
30356
- colorCircle: {
30357
- // Add a white border so that the color circle is visible when
30358
- // the dropdown option is highlighted with its blue background.
30359
- border: `2px solid ${color.offWhite}`,
30360
- borderRadius: "50%",
30361
- width: spacing.large_24,
30362
- height: spacing.large_24
30694
+ deleteButton: {
30695
+ position: "absolute",
30696
+ top: spacing.small_12,
30697
+ right: spacing.small_12
30363
30698
  }
30364
30699
  });
30365
30700
 
@@ -30376,6 +30711,8 @@ const LockedFigureSettings = props => {
30376
30711
  switch (props.type) {
30377
30712
  case "point":
30378
30713
  return /*#__PURE__*/React.createElement(LockedPointSettings, props);
30714
+ case "line":
30715
+ return /*#__PURE__*/React.createElement(LockedLineSettings, props);
30379
30716
  }
30380
30717
  return null;
30381
30718
  };
@@ -30389,7 +30726,7 @@ const LockedFiguresSection = props => {
30389
30726
  function addLockedFigure(newFigure) {
30390
30727
  const lockedFigures = figures || [];
30391
30728
  const newProps = {
30392
- lockedFigures: [...lockedFigures, getDefaultFigureForFigureType(newFigure)]
30729
+ lockedFigures: [...lockedFigures, getDefaultFigureForType(newFigure)]
30393
30730
  };
30394
30731
  onChange(newProps);
30395
30732
  }
@@ -30399,34 +30736,22 @@ const LockedFiguresSection = props => {
30399
30736
  lockedFigures: [...lockedFigures.slice(0, index), ...lockedFigures.slice(index + 1)]
30400
30737
  });
30401
30738
  }
30402
- function changeCoord(index, coord) {
30739
+ function changeProps(index,
30740
+ // Omit the type from the figure props so it doesn't think
30741
+ // we're trying to pass in the props for the wrong type.
30742
+ figureProps) {
30403
30743
  const lockedFigures = figures || [];
30404
- const newProps = {
30405
- lockedFigures: [...lockedFigures.slice(0, index), _extends({}, lockedFigures[index], {
30406
- coord
30407
- }), ...lockedFigures.slice(index + 1)]
30744
+ const newFigures = {
30745
+ lockedFigures: [...lockedFigures.slice(0, index), _extends({}, lockedFigures[index], figureProps), ...lockedFigures.slice(index + 1)]
30408
30746
  };
30409
- onChange(newProps);
30410
- }
30411
- function changeColor(index, colorName) {
30412
- const lockedFigures = figures || [];
30413
- const newProps = {
30414
- lockedFigures: [...lockedFigures.slice(0, index), _extends({}, lockedFigures[index], {
30415
- style: _extends({}, lockedFigures[index].style, {
30416
- fill: colorName,
30417
- stroke: colorName
30418
- })
30419
- }), ...lockedFigures.slice(index + 1)]
30420
- };
30421
- onChange(newProps);
30747
+ onChange(newFigures);
30422
30748
  }
30423
30749
  return /*#__PURE__*/React.createElement(View, {
30424
30750
  style: styles$c.container
30425
30751
  }, figures == null ? void 0 : figures.map((figure, index) => /*#__PURE__*/React.createElement(LockedFigureSettings, _extends({
30426
30752
  key: `${uniqueId}-locked-${figure}-${index}`
30427
30753
  }, figure, {
30428
- onChangeColor: color => changeColor(index, color),
30429
- onChangeCoord: coord => changeCoord(index, coord),
30754
+ onChangeProps: newProps => changeProps(index, newProps),
30430
30755
  onRemove: () => removeLockedFigure(index)
30431
30756
  }))), /*#__PURE__*/React.createElement(LockedFigureSelect, {
30432
30757
  id: `${uniqueId}-select`,
@@ -30519,13 +30844,15 @@ class InteractiveGraphEditor extends React.Component {
30519
30844
  DeprecationMixin.UNSAFE_componentWillMount.call(this);
30520
30845
  }
30521
30846
  render() {
30522
- var _this$props$graph$typ, _this$props$graph, _this$props$correct, _this$props$correct2, _this$props$correct3, _this$props$correct4, _this$props$correct5, _this$props$correct6, _this$props$correct7, _this$props$correct8, _this$props$correct9, _this$props$correct10, _this$props$correct11, _this$props$apiOption;
30847
+ var _this$props$graph$typ, _this$props$graph, _this$props$correct, _this$props$correct2, _this$props$correct3, _this$props$correct4, _this$props$correct5, _this$props$correct6, _this$props$correct7, _this$props$correct8, _this$props$correct9, _this$props$correct10, _this$props$correct11, _this$props$apiOption, _this$props$apiOption2;
30523
30848
  let graph;
30524
30849
  let equationString;
30525
30850
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, interactiveSizes.defaultBoxSize);
30526
30851
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
30527
30852
  const sizeClass = containerSizeClass.SMALL;
30528
30853
  if (this.props.valid === true) {
30854
+ const correct = this.props.correct;
30855
+
30529
30856
  // TODO(aria): send these down all at once
30530
30857
  const graphProps = {
30531
30858
  ref: "graph",
@@ -30535,7 +30862,7 @@ class InteractiveGraphEditor extends React.Component {
30535
30862
  step: this.props.step,
30536
30863
  gridStep: gridStep,
30537
30864
  snapStep: snapStep,
30538
- graph: this.props.correct,
30865
+ graph: correct,
30539
30866
  backgroundImage: this.props.backgroundImage,
30540
30867
  markings: this.props.markings,
30541
30868
  showProtractor: this.props.showProtractor,
@@ -30559,6 +30886,10 @@ class InteractiveGraphEditor extends React.Component {
30559
30886
  });
30560
30887
  }
30561
30888
  };
30889
+
30890
+ // This is used to force a remount of the graph component
30891
+ // when there's a significant change
30892
+ const graphKey = `${correct.type}:${correct.numSegments || 0}`;
30562
30893
  graph =
30563
30894
  /*#__PURE__*/
30564
30895
  // There are a bunch of props that renderer.jsx passes to widgets via
@@ -30566,6 +30897,7 @@ class InteractiveGraphEditor extends React.Component {
30566
30897
  // bother passing.
30567
30898
  // @ts-expect-error - TS2769 - No overload matches this call.
30568
30899
  React.createElement(InteractiveGraph, _extends({}, graphProps, {
30900
+ key: graphKey,
30569
30901
  containerSizeClass: sizeClass,
30570
30902
  apiOptions: _extends({}, this.props.apiOptions, {
30571
30903
  isMobile: false
@@ -30761,9 +31093,9 @@ class InteractiveGraphEditor extends React.Component {
30761
31093
  borderWidth: 1
30762
31094
  }
30763
31095
  }, equationString), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "Graph the correct answer in the graph below and ensure the equation or point coordinates displayed represent the correct answer."))), graph,
30764
- // Only show the "Add element" dropdown if the graph is
30765
- // using Mafs.
30766
- this.props.graph && ((_this$props$apiOption = this.props.apiOptions) == null || (_this$props$apiOption = _this$props$apiOption.flags) == null || (_this$props$apiOption = _this$props$apiOption["mafs"]) == null ? void 0 : _this$props$apiOption[this.props.graph.type]) && /*#__PURE__*/React.createElement(LockedFiguresSection, {
31096
+ // Only show the "Add locked figure" dropdown if the graph
31097
+ // is using Mafs.
31098
+ this.props.graph && ((_this$props$apiOption = this.props.apiOptions) == null || (_this$props$apiOption = _this$props$apiOption.flags) == null || (_this$props$apiOption = _this$props$apiOption.mafs) == null ? void 0 : _this$props$apiOption["interactive-graph-locked-features-m1"]) && ((_this$props$apiOption2 = this.props.apiOptions) == null || (_this$props$apiOption2 = _this$props$apiOption2.flags) == null || (_this$props$apiOption2 = _this$props$apiOption2.mafs) == null ? void 0 : _this$props$apiOption2[this.props.graph.type]) && /*#__PURE__*/React.createElement(LockedFiguresSection, {
30767
31099
  figures: this.props.lockedFigures,
30768
31100
  onChange: this.props.onChange
30769
31101
  }));
@@ -30911,7 +31243,7 @@ class FormWrappedTextField extends React.Component {
30911
31243
  className: css(inputBase),
30912
31244
  ref: forwardedRef,
30913
31245
  id: id,
30914
- "data-test-id": testId
31246
+ "data-testid": testId
30915
31247
  })), !leftSideIcon && wrappedIcon);
30916
31248
  }
30917
31249
  }
@@ -31043,7 +31375,7 @@ class Link extends React.Component {
31043
31375
  rel = "noopener noreferrer";
31044
31376
  }
31045
31377
  return /*#__PURE__*/React.createElement(element, _extends({
31046
- "data-test-id": testId
31378
+ "data-testid": testId
31047
31379
  }, otherProps, urlProp, {
31048
31380
  className: css(...delegatedStyles) + delegatedClassName,
31049
31381
  style: inlineStyles,
@@ -31362,7 +31694,7 @@ class Checkbox extends React.Component {
31362
31694
  }, handlers) => /*#__PURE__*/React.createElement("div", _extends({}, handlers, {
31363
31695
  className: css(styles$6.container, focused && styles$6.focused),
31364
31696
  style: style,
31365
- "data-test-id": dataTestId,
31697
+ "data-testid": dataTestId,
31366
31698
  "data-checked": checked
31367
31699
  }), /*#__PURE__*/React.createElement("svg", {
31368
31700
  className: css(styles$6.svg, (disabled || appearDisabled) && styles$6.disabled),
@@ -31609,7 +31941,7 @@ class Option extends React.Component {
31609
31941
  onKeyDown: event => this.handleKeyDown(event),
31610
31942
  "aria-disabled": disabled,
31611
31943
  "aria-label": ariaLabel,
31612
- "data-test-id": testId
31944
+ "data-testid": testId
31613
31945
  }, optionRenderer && optionRenderer(
31614
31946
  /*#__PURE__*/
31615
31947
  /**
@@ -31985,8 +32317,8 @@ class QuestionMarkers extends React.Component {
31985
32317
  // Open answer selection dropdown for each of the specified markers.
31986
32318
  indices.forEach(index => {
31987
32319
  if (this._markers[index]) {
31988
- // @ts-expect-error - TS2533 - Object is possibly 'null' or 'undefined'.
31989
- this._markers[index].openDropdown();
32320
+ var _this$_markers$index;
32321
+ (_this$_markers$index = this._markers[index]) == null || _this$_markers$index.openDropdown();
31990
32322
  }
31991
32323
  });
31992
32324
  }
@@ -32989,6 +33321,7 @@ NumberLineEditor.defaultProps = {
32989
33321
  showTooltips: false
32990
33322
  };
32991
33323
 
33324
+ /* eslint-disable jsx-a11y/anchor-is-valid, react/sort-comp */
32992
33325
  const {
32993
33326
  ButtonGroup,
32994
33327
  InfoTip: InfoTip$7,
@@ -33005,6 +33338,9 @@ const {
33005
33338
  const {
33006
33339
  firstNumericalParse
33007
33340
  } = Util;
33341
+
33342
+ // NOTE(john): Copied from perseus-types.d.ts in the Perseus package.
33343
+ // I'm unable to find a good way of importing these types into this project.
33008
33344
  const answerFormButtons = [{
33009
33345
  title: "Integers",
33010
33346
  value: "integer",
@@ -33041,28 +33377,15 @@ const initAnswer = status => {
33041
33377
  maxError: null
33042
33378
  };
33043
33379
  };
33044
- const NumericInputEditor = createReactClass({
33045
- displayName: "NumericInputEditor",
33046
- propTypes: _extends({}, Changeable.propTypes),
33047
- statics: {
33048
- widgetName: "numeric-input"
33049
- },
33050
- getDefaultProps: function () {
33051
- return {
33052
- answers: [initAnswer("correct")],
33053
- size: "normal",
33054
- coefficient: false,
33055
- labelText: "",
33056
- rightAlign: false
33057
- };
33058
- },
33059
- getInitialState: function () {
33060
- return {
33380
+ class NumericInputEditor extends React.Component {
33381
+ constructor(props) {
33382
+ super(props);
33383
+ this.state = {
33061
33384
  lastStatus: "wrong",
33062
33385
  showOptions: _.map(this.props.answers, () => false)
33063
33386
  };
33064
- },
33065
- render: function () {
33387
+ }
33388
+ render() {
33066
33389
  const answers = this.props.answers;
33067
33390
  const unsimplifiedAnswers = i => /*#__PURE__*/React.createElement("div", {
33068
33391
  className: "perseus-widget-row"
@@ -33079,14 +33402,14 @@ const NumericInputEditor = createReactClass({
33079
33402
  value: "enforced",
33080
33403
  content: "wrong"
33081
33404
  }],
33082
- onChange: this.updateAnswer(i, "simplify")
33405
+ onChange: this.updateAnswer(i, "simplify") || (() => {})
33083
33406
  }), /*#__PURE__*/React.createElement(InfoTip$7, null, /*#__PURE__*/React.createElement("p", null, "Normally select \"ungraded\". This will give the user a message saying the answer is correct but not simplified. The user will then have to simplify it and re-enter, but will not be penalized. (5th grade and after)"), /*#__PURE__*/React.createElement("p", null, "Select \"accepted\" only if the user is not expected to know how to simplify fractions yet. (Anything prior to 5th grade)"), /*#__PURE__*/React.createElement("p", null, "Select \"wrong\" ", /*#__PURE__*/React.createElement("em", null, "only"), " if we are specifically assessing the ability to simplify.")));
33084
33407
  const suggestedAnswerTypes = i => /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
33085
33408
  className: "perseus-widget-row"
33086
33409
  }, /*#__PURE__*/React.createElement("label", null, "Choose the suggested answer formats"), /*#__PURE__*/React.createElement(MultiButtonGroup, {
33087
33410
  buttons: answerFormButtons,
33088
33411
  values: answers[i]["answerForms"],
33089
- onChange: this.updateAnswer(i, "answerForms")
33412
+ onChange: this.updateAnswer(i, "answerForms") || (() => {})
33090
33413
  }), /*#__PURE__*/React.createElement(InfoTip$7, null, /*#__PURE__*/React.createElement("p", null, "Formats will be autoselected for you based on the given answer; to show no suggested formats and accept all types, simply have a decimal/integer be the answer. Values with \u03C0 will have format \"pi\", and values that are fractions will have some subset (mixed will be \"mixed\" and \"proper\"; improper/proper will both be \"improper\" and \"proper\"). If you would like to specify that it is only a proper fraction (or only a mixed/improper fraction), deselect the other format. Except for specific cases, you should not need to change the autoselected formats."), /*#__PURE__*/React.createElement("p", null, "To restrict the answer to ", /*#__PURE__*/React.createElement("em", null, "only"), " an improper fraction (i.e. 7/4), select the improper fraction and toggle \"strict\" to true. This ", /*#__PURE__*/React.createElement("b", null, "will not"), " ", "accept 1.75 as an answer.", " "), /*#__PURE__*/React.createElement("p", null, "Unless you are testing that specific skill, please do not restrict the answer format."))), /*#__PURE__*/React.createElement("div", {
33091
33414
  className: "perseus-widget-row"
33092
33415
  }, /*#__PURE__*/React.createElement(PropCheckBox$3, {
@@ -33172,7 +33495,7 @@ const NumericInputEditor = createReactClass({
33172
33495
  value: answer.value,
33173
33496
  className: "numeric-input-value",
33174
33497
  placeholder: "answer",
33175
- format: _.last(answer.answerForms),
33498
+ format: _.last(answer.answerForms || []),
33176
33499
  onFormatChange: (newValue, format) => {
33177
33500
  // NOTE(charlie): The mobile web expression
33178
33501
  // editor relies on this automatic answer
@@ -33196,13 +33519,13 @@ const NumericInputEditor = createReactClass({
33196
33519
  forms = ["proper", "improper"];
33197
33520
  }
33198
33521
  this.updateAnswer(i, {
33199
- value: firstNumericalParse(newValue),
33522
+ value: firstNumericalParse(newValue, this.context.strings),
33200
33523
  answerForms: forms
33201
33524
  });
33202
33525
  },
33203
33526
  onChange: newValue => {
33204
33527
  this.updateAnswer(i, {
33205
- value: firstNumericalParse(newValue)
33528
+ value: firstNumericalParse(newValue, this.context.strings)
33206
33529
  });
33207
33530
  }
33208
33531
  }), answer.strict && /*#__PURE__*/React.createElement("div", {
@@ -33218,7 +33541,7 @@ const NumericInputEditor = createReactClass({
33218
33541
  }, "\xB1"), /*#__PURE__*/React.createElement(NumberInput$5, {
33219
33542
  placeholder: 0,
33220
33543
  value: answers[i]["maxError"],
33221
- format: _.last(answer.answerForms),
33544
+ format: _.last(answer.answerForms || []),
33222
33545
  onChange: this.updateAnswer(i, "maxError")
33223
33546
  })) : null, /*#__PURE__*/React.createElement("div", {
33224
33547
  className: "value-divider"
@@ -33226,19 +33549,19 @@ const NumericInputEditor = createReactClass({
33226
33549
  href: "javascript:void(0)",
33227
33550
  className: "answer-status " + answer.status,
33228
33551
  onClick: () => this.onStatusChange(i),
33229
- onKeyDown: e => this.onSpace(e, this.onStatusChange, i)
33552
+ onKeyDown: e => this.onSpace(e, this.onStatusChange)
33230
33553
  }, answer.status), /*#__PURE__*/React.createElement("a", {
33231
33554
  href: "javascript:void(0)",
33232
33555
  className: "answer-trash",
33233
33556
  "aria-label": "Delete answer",
33234
33557
  onClick: () => this.onTrashAnswer(i),
33235
- onKeyDown: e => this.onSpace(e, this.onTrashAnswer, i)
33558
+ onKeyDown: e => this.onSpace(e, this.onTrashAnswer)
33236
33559
  }, /*#__PURE__*/React.createElement(InlineIcon$2, iconTrash$1)), /*#__PURE__*/React.createElement("a", {
33237
33560
  href: "javascript:void(0)",
33238
33561
  className: "options-toggle",
33239
33562
  "aria-label": "Toggle options",
33240
33563
  onClick: () => this.onToggleOptions(i),
33241
- onKeyDown: e => this.onSpace(e, this.onToggleOptions, i)
33564
+ onKeyDown: e => this.onSpace(e, this.onToggleOptions)
33242
33565
  }, /*#__PURE__*/React.createElement(InlineIcon$2, iconGear))), /*#__PURE__*/React.createElement("div", {
33243
33566
  className: "input-answer-editor-message"
33244
33567
  }, editor), this.state.showOptions[i] && /*#__PURE__*/React.createElement("div", {
@@ -33252,18 +33575,18 @@ const NumericInputEditor = createReactClass({
33252
33575
  }, "User input"), /*#__PURE__*/React.createElement("div", {
33253
33576
  className: "msg-title"
33254
33577
  }, "Message shown to user on attempt"), generateInputAnswerEditors(), addAnswerButton, inputSize, rightAlign, coefficientCheck, labelText);
33255
- },
33578
+ }
33256
33579
  change(...args) {
33257
33580
  return Changeable.change.apply(this, args);
33258
- },
33259
- onToggleOptions: function (choiceIndex) {
33581
+ }
33582
+ onToggleOptions(choiceIndex) {
33260
33583
  const showOptions = this.state.showOptions.slice();
33261
33584
  showOptions[choiceIndex] = !showOptions[choiceIndex];
33262
33585
  this.setState({
33263
33586
  showOptions: showOptions
33264
33587
  });
33265
- },
33266
- onTrashAnswer: function (choiceIndex) {
33588
+ }
33589
+ onTrashAnswer(choiceIndex) {
33267
33590
  if (choiceIndex >= 0 && choiceIndex < this.props.answers.length) {
33268
33591
  const answers = this.props.answers.slice(0);
33269
33592
  answers.splice(choiceIndex, 1);
@@ -33271,16 +33594,16 @@ const NumericInputEditor = createReactClass({
33271
33594
  answers: answers
33272
33595
  });
33273
33596
  }
33274
- },
33275
- onSpace: function (e, callback) {
33597
+ }
33598
+ onSpace(e, callback) {
33276
33599
  if (e.key === " ") {
33277
33600
  e.preventDefault(); // prevent page shifting
33278
33601
  // eslint-disable-next-line prefer-rest-params
33279
33602
  const args = _.toArray(arguments).slice(2);
33280
33603
  callback.apply(this, args);
33281
33604
  }
33282
- },
33283
- onStatusChange: function (choiceIndex) {
33605
+ }
33606
+ onStatusChange(choiceIndex) {
33284
33607
  const statuses = ["wrong", "ungraded", "correct"];
33285
33608
  const answers = this.props.answers;
33286
33609
  const i = _.indexOf(statuses, answers[choiceIndex].status);
@@ -33289,8 +33612,8 @@ const NumericInputEditor = createReactClass({
33289
33612
  status: newStatus,
33290
33613
  simplify: newStatus === "correct" ? "required" : "accepted"
33291
33614
  });
33292
- },
33293
- updateAnswer: function (choiceIndex, update) {
33615
+ }
33616
+ updateAnswer(choiceIndex, update) {
33294
33617
  if (!_.isObject(update)) {
33295
33618
  return _.partial((choiceIndex, key, value) => {
33296
33619
  const update = {};
@@ -33298,7 +33621,9 @@ const NumericInputEditor = createReactClass({
33298
33621
  this.updateAnswer(choiceIndex, update);
33299
33622
  }, choiceIndex, update);
33300
33623
  }
33301
- let answers = _.clone(this.props.answers);
33624
+ let answers = [
33625
+ // Have to do this to remove the `readonly` state from the prop
33626
+ ...this.props.answers];
33302
33627
 
33303
33628
  // Don't bother to make a new answer box unless we are editing the last
33304
33629
  // one.
@@ -33311,15 +33636,15 @@ const NumericInputEditor = createReactClass({
33311
33636
  this.props.onChange({
33312
33637
  answers: answers
33313
33638
  });
33314
- },
33315
- addAnswer: function () {
33639
+ }
33640
+ addAnswer() {
33316
33641
  const lastAnswer = initAnswer(this.state.lastStatus);
33317
33642
  const answers = this.props.answers.concat(lastAnswer);
33318
33643
  this.props.onChange({
33319
33644
  answers: answers
33320
33645
  });
33321
- },
33322
- getSaveWarnings: function () {
33646
+ }
33647
+ getSaveWarnings() {
33323
33648
  // Filter out all the empty answers
33324
33649
  const warnings = [];
33325
33650
  // TODO(emily): This doesn't actually work, because the value is either
@@ -33337,11 +33662,21 @@ const NumericInputEditor = createReactClass({
33337
33662
  }
33338
33663
  });
33339
33664
  return warnings;
33340
- },
33665
+ }
33341
33666
  serialize() {
33342
33667
  return EditorJsonify.serialize.call(this);
33343
33668
  }
33344
- });
33669
+ }
33670
+ NumericInputEditor.contextType = PerseusI18nContext;
33671
+ NumericInputEditor.widgetName = "numeric-input";
33672
+ NumericInputEditor.displayName = "NumericInputEditor";
33673
+ NumericInputEditor.defaultProps = {
33674
+ answers: [initAnswer("correct")],
33675
+ size: "normal",
33676
+ coefficient: false,
33677
+ labelText: "",
33678
+ rightAlign: false
33679
+ };
33345
33680
 
33346
33681
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
33347
33682
  const {
@@ -34014,9 +34349,7 @@ class ChoiceEditor extends React.Component {
34014
34349
  apiOptions: this.props.apiOptions,
34015
34350
  content: this.props.choice.clue || "",
34016
34351
  widgetEnabled: false,
34017
- placeholder: i18n$1._("Why is this choice %(correctOrIncorrect)s?", {
34018
- correctOrIncorrect: checkedClass
34019
- }),
34352
+ placeholder: `Why is this choice ${checkedClass}?`,
34020
34353
  onChange: this.props.onClueChange
34021
34354
  });
34022
34355
  const deleteLink = /*#__PURE__*/React.createElement("a", {
@@ -34261,7 +34594,7 @@ RadioEditor.propTypes = _extends({}, Changeable.propTypes, {
34261
34594
  });
34262
34595
  RadioEditor.widgetName = "radio";
34263
34596
  RadioEditor.defaultProps = {
34264
- choices: [{}, {}],
34597
+ choices: [{}, {}, {}, {}],
34265
34598
  displayCount: null,
34266
34599
  randomize: false,
34267
34600
  hasNoneOfTheAbove: false,