@khanacademy/perseus-editor 5.0.2 → 5.1.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
@@ -9,7 +9,7 @@ import Clickable from '@khanacademy/wonder-blocks-clickable';
9
9
  import Color from '@khanacademy/wonder-blocks-color';
10
10
  import { StyleSheet, css } from 'aphrodite';
11
11
  import $ from 'jquery';
12
- import { View } from '@khanacademy/wonder-blocks-core';
12
+ import { View, useUniqueIdWithMock } from '@khanacademy/wonder-blocks-core';
13
13
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
14
14
  import caretRight from '@phosphor-icons/core/bold/caret-right-bold.svg';
15
15
  import classNames from 'classnames';
@@ -18,26 +18,33 @@ import { point, number } from '@khanacademy/kmath';
18
18
  import * as KAS from '@khanacademy/kas';
19
19
  import { isTruthy } from '@khanacademy/wonder-stuff-core';
20
20
  import { createMathField } from '@khanacademy/math-input';
21
+ import { SingleSelect, OptionItem, ActionMenu, ActionItem } from '@khanacademy/wonder-blocks-dropdown';
22
+ import { TextField, Checkbox as Checkbox$1 } from '@khanacademy/wonder-blocks-form';
21
23
  import Spacing from '@khanacademy/wonder-blocks-spacing';
22
- import * as Typography from '@khanacademy/wonder-blocks-typography';
24
+ import { LabelSmall, LabelLarge, LabelMedium, BodyMonospace } from '@khanacademy/wonder-blocks-typography';
25
+ import { spacing, color } from '@khanacademy/wonder-blocks-tokens';
26
+ import IconButton from '@khanacademy/wonder-blocks-icon-button';
27
+ import trashIcon from '@phosphor-icons/core/bold/trash-bold.svg';
23
28
  import Button from '@khanacademy/wonder-blocks-button';
24
29
  import * as i18n$1 from '@khanacademy/wonder-blocks-i18n';
25
30
 
26
31
  // This file is processed by a Rollup plugin (replace) to inject the production
27
32
  const libName = "@khanacademy/perseus-editor";
28
- const libVersion = "5.0.2";
33
+ const libVersion = "5.1.0";
29
34
  addLibraryVersionToPerseusDebug(libName, libVersion);
30
35
 
31
36
  function _extends() {
32
37
  _extends = Object.assign ? Object.assign.bind() : function (target) {
33
38
  for (var i = 1; i < arguments.length; i++) {
34
39
  var source = arguments[i];
40
+
35
41
  for (var key in source) {
36
42
  if (Object.prototype.hasOwnProperty.call(source, key)) {
37
43
  target[key] = source[key];
38
44
  }
39
45
  }
40
46
  }
47
+
41
48
  return target;
42
49
  };
43
50
  return _extends.apply(this, arguments);
@@ -219,7 +226,7 @@ class SectionControlButton extends React.Component {
219
226
  role: "button",
220
227
  hideDefaultFocusRing: true,
221
228
  "aria-label": title,
222
- style: styles$g.button
229
+ style: styles$q.button
223
230
  }, ({
224
231
  hovered,
225
232
  focused,
@@ -227,7 +234,7 @@ class SectionControlButton extends React.Component {
227
234
  }) => /*#__PURE__*/React.createElement(InlineIcon$b, icon));
228
235
  }
229
236
  }
230
- const styles$g = StyleSheet.create({
237
+ const styles$q = StyleSheet.create({
231
238
  button: {
232
239
  backgroundColor: Color.gold,
233
240
  margin: 2,
@@ -20749,19 +20756,19 @@ class TexErrorView extends React.Component {
20749
20756
  const {
20750
20757
  showErrors
20751
20758
  } = this.state;
20752
- const iconStyle = showErrors ? styles$f.iconExpanded : styles$f.iconCollapsed;
20759
+ const iconStyle = showErrors ? styles$p.iconExpanded : styles$p.iconCollapsed;
20753
20760
  return /*#__PURE__*/React.createElement(View, {
20754
- style: styles$f.errorContainer
20761
+ style: styles$p.errorContainer
20755
20762
  }, /*#__PURE__*/React.createElement(View, {
20756
- style: styles$f.title,
20763
+ style: styles$p.title,
20757
20764
  onClick: this.handleToggleTexErrors
20758
20765
  }, /*#__PURE__*/React.createElement(PhosphorIcon, {
20759
20766
  icon: caretRight,
20760
20767
  style: iconStyle
20761
20768
  }), "\xA0 TeX Errors (", errorList.length, ")"), showErrors && /*#__PURE__*/React.createElement(View, {
20762
- style: styles$f.errorExplanation
20769
+ style: styles$p.errorExplanation
20763
20770
  }, "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, {
20764
- style: styles$f.error,
20771
+ style: styles$p.error,
20765
20772
  key: index
20766
20773
  }, /*#__PURE__*/React.createElement(View, {
20767
20774
  style: {
@@ -20770,7 +20777,7 @@ class TexErrorView extends React.Component {
20770
20777
  }, e.math), /*#__PURE__*/React.createElement(View, null, e.message))));
20771
20778
  }
20772
20779
  }
20773
- const styles$f = StyleSheet.create({
20780
+ const styles$p = StyleSheet.create({
20774
20781
  title: {
20775
20782
  backgroundColor: "#eee",
20776
20783
  fontSize: "1.25em",
@@ -24363,11 +24370,13 @@ function _objectWithoutPropertiesLoose(source, excluded) {
24363
24370
  var target = {};
24364
24371
  var sourceKeys = Object.keys(source);
24365
24372
  var key, i;
24373
+
24366
24374
  for (i = 0; i < sourceKeys.length; i++) {
24367
24375
  key = sourceKeys[i];
24368
24376
  if (excluded.indexOf(key) >= 0) continue;
24369
24377
  target[key] = source[key];
24370
24378
  }
24379
+
24371
24380
  return target;
24372
24381
  }
24373
24382
 
@@ -24599,7 +24608,7 @@ class SimpleButton extends React.Component {
24599
24608
  } = _this$props,
24600
24609
  otherProps = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
24601
24610
  return /*#__PURE__*/React.createElement("div", _extends({
24602
- className: css(styles$e.baseButton, color === "green" && styles$e.green, color === "orange" && styles$e.orange),
24611
+ className: css(styles$o.baseButton, color === "green" && styles$o.green, color === "orange" && styles$o.orange),
24603
24612
  onClick: onClick
24604
24613
  }, otherProps), children);
24605
24614
  }
@@ -24614,7 +24623,7 @@ SimpleButton.defaultProps = {
24614
24623
  color: "green",
24615
24624
  onClick: () => {}
24616
24625
  };
24617
- const styles$e = StyleSheet.create({
24626
+ const styles$o = StyleSheet.create({
24618
24627
  baseButton: {
24619
24628
  top: "0",
24620
24629
  fontSize: "11px",
@@ -24834,23 +24843,23 @@ const LeafContainer = ({
24834
24843
  }) => {
24835
24844
  const hasPreviewHeading = shape.type === "content" || shape.type === "hint";
24836
24845
  const previewHeading = hasPreviewHeading && /*#__PURE__*/React.createElement("div", {
24837
- className: css(styles$d.containerHeader)
24846
+ className: css(styles$n.containerHeader)
24838
24847
  }, /*#__PURE__*/React.createElement(Header, {
24839
24848
  depth: path.length,
24840
- className: css(styles$d.containerTitle)
24849
+ className: css(styles$n.containerTitle)
24841
24850
  }, capitalize(name)));
24842
24851
  return /*#__PURE__*/React.createElement("div", {
24843
- className: css(styles$d.container)
24852
+ className: css(styles$n.container)
24844
24853
  }, /*#__PURE__*/React.createElement("span", {
24845
- className: css(styles$d.row, styles$d.rowHeading)
24854
+ className: css(styles$n.row, styles$n.rowHeading)
24846
24855
  }, /*#__PURE__*/React.createElement("div", {
24847
- className: css(styles$d.columnLeft)
24856
+ className: css(styles$n.columnLeft)
24848
24857
  }, /*#__PURE__*/React.createElement("div", {
24849
- className: "pod-title " + css(styles$d.containerHeader)
24858
+ className: "pod-title " + css(styles$n.containerHeader)
24850
24859
  }, /*#__PURE__*/React.createElement("div", {
24851
- className: css(styles$d.containerTitle)
24860
+ className: css(styles$n.containerTitle)
24852
24861
  }, capitalize(name)), controls)), /*#__PURE__*/React.createElement("div", {
24853
- className: css(styles$d.columnRight)
24862
+ className: css(styles$n.columnRight)
24854
24863
  }, previewHeading)), children);
24855
24864
  };
24856
24865
  const ArrayContainer = props => {
@@ -24863,11 +24872,11 @@ const ArrayContainer = props => {
24863
24872
  actions
24864
24873
  } = props;
24865
24874
  return /*#__PURE__*/React.createElement("div", {
24866
- className: css(styles$d.container)
24875
+ className: css(styles$n.container)
24867
24876
  }, controls && /*#__PURE__*/React.createElement("div", {
24868
- className: css(styles$d.columnLeft, styles$d.containerHeader)
24877
+ className: css(styles$n.columnLeft, styles$n.containerHeader)
24869
24878
  }, controls), /*#__PURE__*/React.createElement("div", null, children), /*#__PURE__*/React.createElement("div", {
24870
- className: css(styles$d.columnLeft)
24879
+ className: css(styles$n.columnLeft)
24871
24880
  }, /*#__PURE__*/React.createElement("a", {
24872
24881
  href: "javascript:void 0",
24873
24882
  onClick: () => actions.addArrayElement(path, shape.elementShape)
@@ -24880,28 +24889,28 @@ const ObjectContainer = ({
24880
24889
  path
24881
24890
  }) => {
24882
24891
  const headingEditor = /*#__PURE__*/React.createElement("div", {
24883
- className: css(styles$d.containerHeader)
24892
+ className: css(styles$n.containerHeader)
24884
24893
  }, /*#__PURE__*/React.createElement(Header, {
24885
24894
  depth: path.length,
24886
- className: css(styles$d.containerTitle)
24895
+ className: css(styles$n.containerTitle)
24887
24896
  }, capitalize(name)), controls);
24888
24897
  const headingPreview = (name || controls) && /*#__PURE__*/React.createElement("div", {
24889
- className: css(styles$d.containerHeader, styles$d.previewCollectionHeader)
24898
+ className: css(styles$n.containerHeader, styles$n.previewCollectionHeader)
24890
24899
  }, /*#__PURE__*/React.createElement(Header, {
24891
24900
  depth: path.length,
24892
- className: css(styles$d.containerTitle)
24901
+ className: css(styles$n.containerTitle)
24893
24902
  }, capitalize(name)));
24894
24903
  const hasBothHeadings = headingEditor && headingPreview;
24895
24904
  return /*#__PURE__*/React.createElement("div", {
24896
- className: css(styles$d.container)
24905
+ className: css(styles$n.container)
24897
24906
  }, hasBothHeadings && /*#__PURE__*/React.createElement("span", {
24898
- className: css(styles$d.row)
24907
+ className: css(styles$n.row)
24899
24908
  }, /*#__PURE__*/React.createElement("div", {
24900
- className: css(styles$d.columnLeft)
24909
+ className: css(styles$n.columnLeft)
24901
24910
  }, headingEditor), /*#__PURE__*/React.createElement("div", {
24902
- className: css(styles$d.columnRight)
24911
+ className: css(styles$n.columnRight)
24903
24912
  }, headingPreview)), /*#__PURE__*/React.createElement("div", {
24904
- className: css(path.length > 0 && styles$d.contentIndent)
24913
+ className: css(path.length > 0 && styles$n.contentIndent)
24905
24914
  }, children));
24906
24915
  };
24907
24916
 
@@ -25024,11 +25033,11 @@ const ItemNodeContent = withStickiness(props => {
25024
25033
  className: "framework-perseus"
25025
25034
  }, lens(renderers).get(path));
25026
25035
  return /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("div", {
25027
- className: css(styles$d.row)
25036
+ className: css(styles$n.row)
25028
25037
  }, /*#__PURE__*/React.createElement("div", {
25029
- className: css(styles$d.columnLeft)
25038
+ className: css(styles$n.columnLeft)
25030
25039
  }, /*#__PURE__*/React.createElement("div", {
25031
- className: css(sticky && styles$d.sticky)
25040
+ className: css(sticky && styles$n.sticky)
25032
25041
  },
25033
25042
  /*#__PURE__*/
25034
25043
  // @ts-expect-error - TS2769 - No overload matches this call.
@@ -25036,9 +25045,9 @@ const ItemNodeContent = withStickiness(props => {
25036
25045
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
25037
25046
  apiOptions: apiOptions
25038
25047
  })))), /*#__PURE__*/React.createElement("div", {
25039
- className: css(styles$d.columnRight)
25048
+ className: css(styles$n.columnRight)
25040
25049
  }, /*#__PURE__*/React.createElement("div", {
25041
- className: css(sticky && styles$d.sticky)
25050
+ className: css(sticky && styles$n.sticky)
25042
25051
  }, preview))));
25043
25052
  });
25044
25053
  const HintNodeContent = withStickiness(props => {
@@ -25054,13 +25063,13 @@ const HintNodeContent = withStickiness(props => {
25054
25063
  className: "framework-perseus"
25055
25064
  }, lens(renderers).get(path));
25056
25065
  return /*#__PURE__*/React.createElement("div", {
25057
- className: css(styles$d.row)
25066
+ className: css(styles$n.row)
25058
25067
  }, /*#__PURE__*/React.createElement("div", {
25059
- className: css(styles$d.columnLeft)
25068
+ className: css(styles$n.columnLeft)
25060
25069
  }, /*#__PURE__*/React.createElement("div", {
25061
- className: css(sticky && styles$d.sticky)
25070
+ className: css(sticky && styles$n.sticky)
25062
25071
  }, /*#__PURE__*/React.createElement(HintEditor, _extends({}, data, {
25063
- className: css(styles$d.hintEditor),
25072
+ className: css(styles$n.hintEditor),
25064
25073
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
25065
25074
  apiOptions: apiOptions,
25066
25075
  showTitle: false,
@@ -25075,9 +25084,9 @@ const HintNodeContent = withStickiness(props => {
25075
25084
  isFirst: true,
25076
25085
  isLast: true
25077
25086
  })))), /*#__PURE__*/React.createElement("div", {
25078
- className: css(styles$d.columnRight)
25087
+ className: css(styles$n.columnRight)
25079
25088
  }, /*#__PURE__*/React.createElement("div", {
25080
- className: css(sticky && styles$d.sticky)
25089
+ className: css(sticky && styles$n.sticky)
25081
25090
  }, preview)));
25082
25091
  });
25083
25092
  const TagsNodeContent = props => {
@@ -25094,9 +25103,9 @@ const TagsNodeContent = props => {
25094
25103
  return null;
25095
25104
  }
25096
25105
  return /*#__PURE__*/React.createElement("div", {
25097
- className: css(styles$d.columnLeft)
25106
+ className: css(styles$n.columnLeft)
25098
25107
  }, /*#__PURE__*/React.createElement("div", {
25099
- className: css(styles$d.tagsEditor)
25108
+ className: css(styles$n.tagsEditor)
25100
25109
  }, /*#__PURE__*/React.createElement(GroupMetadataEditor, {
25101
25110
  value: data,
25102
25111
  onChange: newVal => actions.setValueAtPath(path, newVal),
@@ -25127,23 +25136,23 @@ const ArrayNodeContent = props => {
25127
25136
  const subpath = path.concat(i);
25128
25137
  const controls = [i > 0 && /*#__PURE__*/React.createElement("div", {
25129
25138
  key: "moveArrayElementUp",
25130
- className: css(styles$d.control)
25139
+ className: css(styles$n.control)
25131
25140
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25132
25141
  color: "orange",
25133
25142
  title: "Move up",
25134
25143
  onClick: () => actions.moveArrayElementUp(subpath)
25135
25144
  }, /*#__PURE__*/React.createElement("div", {
25136
- className: css(styles$d.verticalFlip)
25145
+ className: css(styles$n.verticalFlip)
25137
25146
  }, /*#__PURE__*/React.createElement(InlineIcon$6, iconChevronDown)))), i < data.length - 1 && /*#__PURE__*/React.createElement("div", {
25138
25147
  key: "moveArrayElementDown",
25139
- className: css(styles$d.control)
25148
+ className: css(styles$n.control)
25140
25149
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25141
25150
  color: "orange",
25142
25151
  title: "Move down",
25143
25152
  onClick: () => actions.moveArrayElementDown(subpath)
25144
25153
  }, /*#__PURE__*/React.createElement(InlineIcon$6, iconChevronDown))), /*#__PURE__*/React.createElement("div", {
25145
25154
  key: "removeArrayElement",
25146
- className: css(styles$d.control)
25155
+ className: css(styles$n.control)
25147
25156
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25148
25157
  color: "orange",
25149
25158
  title: "Delete",
@@ -25151,7 +25160,7 @@ const ArrayNodeContent = props => {
25151
25160
  }, /*#__PURE__*/React.createElement(InlineIcon$6, iconTrash$4)))];
25152
25161
  return /*#__PURE__*/React.createElement("div", {
25153
25162
  key: i,
25154
- className: css(styles$d.arrayElement, !elementIsLeaf && styles$d.arrayElementAndNotLeaf)
25163
+ className: css(styles$n.arrayElement, !elementIsLeaf && styles$n.arrayElementAndNotLeaf)
25155
25164
  }, /*#__PURE__*/React.createElement(NodeContainer, _extends({}, otherProps, {
25156
25165
  key: i,
25157
25166
  shape: shape.elementShape,
@@ -25177,7 +25186,7 @@ const ObjectNodeContent = props => {
25177
25186
  // made sense to the shape's author is the order in which we'll iterate :)
25178
25187
  const children = Object.keys(shape.shape).map(subkey => /*#__PURE__*/React.createElement("div", {
25179
25188
  key: subkey,
25180
- className: css(styles$d.objectElement)
25189
+ className: css(styles$n.objectElement)
25181
25190
  }, /*#__PURE__*/React.createElement(NodeContainer, _extends({}, otherProps, {
25182
25191
  shape: shape.shape[subkey],
25183
25192
  data: data[subkey],
@@ -25344,7 +25353,7 @@ class MultiRendererEditor extends React.Component {
25344
25353
  }, this._renderContent());
25345
25354
  }
25346
25355
  }
25347
- const styles$d = StyleSheet.create({
25356
+ const styles$n = StyleSheet.create({
25348
25357
  // This is used in a number of places throughout this file.
25349
25358
  container: {},
25350
25359
  // eslint-disable-next-line react-native/no-unused-styles
@@ -25496,47 +25505,47 @@ const StatefulArticleEditor = createReactClass({
25496
25505
  screen
25497
25506
  } = this.state;
25498
25507
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
25499
- style: styles$c.controlBar
25508
+ style: styles$m.controlBar
25500
25509
  }, /*#__PURE__*/React.createElement("span", {
25501
- style: styles$c.controls
25510
+ style: styles$m.controls
25502
25511
  }, "Mode:", " ", /*#__PURE__*/React.createElement("span", {
25503
25512
  onClick: () => this.setState({
25504
25513
  mode: "edit"
25505
25514
  }),
25506
- style: mode === "edit" ? styles$c.controlSelected : styles$c.control
25515
+ style: mode === "edit" ? styles$m.controlSelected : styles$m.control
25507
25516
  }, "EDIT"), " | ", /*#__PURE__*/React.createElement("span", {
25508
25517
  onClick: () => this.setState({
25509
25518
  mode: "preview"
25510
25519
  }),
25511
- style: mode === "preview" ? styles$c.controlSelected : styles$c.control
25520
+ style: mode === "preview" ? styles$m.controlSelected : styles$m.control
25512
25521
  }, "PREVIEW"), " | ", /*#__PURE__*/React.createElement("span", {
25513
25522
  onClick: () => this.setState({
25514
25523
  mode: "json"
25515
25524
  }),
25516
- style: mode === "json" ? styles$c.controlSelected : styles$c.control
25525
+ style: mode === "json" ? styles$m.controlSelected : styles$m.control
25517
25526
  }, "JSON")), /*#__PURE__*/React.createElement("span", {
25518
- style: styles$c.controls
25527
+ style: styles$m.controls
25519
25528
  }, "Screen:", " ", /*#__PURE__*/React.createElement("span", {
25520
25529
  onClick: () => this.setState({
25521
25530
  screen: "phone"
25522
25531
  }),
25523
- style: screen === "phone" ? styles$c.controlSelected : styles$c.control
25532
+ style: screen === "phone" ? styles$m.controlSelected : styles$m.control
25524
25533
  }, "PHONE"), " | ", /*#__PURE__*/React.createElement("span", {
25525
25534
  onClick: () => this.setState({
25526
25535
  screen: "tablet"
25527
25536
  }),
25528
- style: screen === "tablet" ? styles$c.controlSelected : styles$c.control
25537
+ style: screen === "tablet" ? styles$m.controlSelected : styles$m.control
25529
25538
  }, "TABLET"), " | ", /*#__PURE__*/React.createElement("span", {
25530
25539
  onClick: () => this.setState({
25531
25540
  screen: "desktop"
25532
25541
  }),
25533
- style: screen === "desktop" ? styles$c.controlSelected : styles$c.control
25542
+ style: screen === "desktop" ? styles$m.controlSelected : styles$m.control
25534
25543
  }, "DESKTOP"))), /*#__PURE__*/React.createElement("div", {
25535
- style: styles$c.editor
25544
+ style: styles$m.editor
25536
25545
  }, /*#__PURE__*/React.createElement(ArticleEditor, this.state)));
25537
25546
  }
25538
25547
  });
25539
- const styles$c = {
25548
+ const styles$m = {
25540
25549
  controlBar: {
25541
25550
  marginBottom: 25,
25542
25551
  marginTop: 5
@@ -27025,10 +27034,10 @@ class GradedGroupEditor extends React.Component {
27025
27034
  }, /*#__PURE__*/React.createElement("div", {
27026
27035
  className: "perseus-widget-row"
27027
27036
  }, /*#__PURE__*/React.createElement("label", {
27028
- className: css(styles$b.title)
27037
+ className: css(styles$l.title)
27029
27038
  }, "Title:", " ", /*#__PURE__*/React.createElement(TextInput$6, {
27030
27039
  value: this.props.title,
27031
- className: css(styles$b.input)
27040
+ className: css(styles$l.input)
27032
27041
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
27033
27042
  ,
27034
27043
  onChange: this.change("title")
@@ -27053,7 +27062,7 @@ class GradedGroupEditor extends React.Component {
27053
27062
  }, /*#__PURE__*/React.createElement(InlineIcon$4, iconPlus$1), " Add a hint"), this.props.hint && /*#__PURE__*/React.createElement("div", {
27054
27063
  className: "perseus-hint-editor"
27055
27064
  }, /*#__PURE__*/React.createElement("div", {
27056
- className: css(styles$b.hintsTitle)
27065
+ className: css(styles$l.hintsTitle)
27057
27066
  }, "Hint"), /*#__PURE__*/React.createElement(Editor, {
27058
27067
  ref: this.hintEditor,
27059
27068
  content: this.props.hint ? this.props.hint.content : "",
@@ -27090,7 +27099,7 @@ GradedGroupEditor.defaultProps = {
27090
27099
  images: {},
27091
27100
  hint: null
27092
27101
  };
27093
- const styles$b = StyleSheet.create({
27102
+ const styles$l = StyleSheet.create({
27094
27103
  title: {
27095
27104
  fontSize: 18,
27096
27105
  fontWeight: "bold"
@@ -28422,13 +28431,13 @@ function MathquillInput(props) {
28422
28431
  }
28423
28432
  });
28424
28433
  return /*#__PURE__*/React.createElement(View, {
28425
- style: styles$a.outerWrapper
28434
+ style: styles$k.outerWrapper
28426
28435
  }, /*#__PURE__*/React.createElement("span", {
28427
28436
  ref: mathFieldWrapperRef,
28428
28437
  className: "perseus-math-input mq-editable-field mq-math-mode"
28429
28438
  }));
28430
28439
  }
28431
- const styles$a = StyleSheet.create({
28440
+ const styles$k = StyleSheet.create({
28432
28441
  outerWrapper: {
28433
28442
  display: "inline-block",
28434
28443
  borderStyle: "solid",
@@ -29481,48 +29490,113 @@ const GraphPointsCountSelector = ({
29481
29490
  numPoints: _numPoints = 1,
29482
29491
  onChange
29483
29492
  }) => {
29484
- return /*#__PURE__*/React.createElement("select", {
29485
- value: _numPoints,
29486
- onChange: e => {
29487
- onChange(parsePointCount(e.target.value));
29493
+ return /*#__PURE__*/React.createElement(SingleSelect, {
29494
+ selectedValue: `${_numPoints}`,
29495
+ onChange: newValue => {
29496
+ onChange(parsePointCount(newValue));
29488
29497
  }
29489
- }, [...Array(7).keys()].map(n => /*#__PURE__*/React.createElement("option", {
29498
+ // Never uses placeholder, always has value
29499
+ ,
29500
+ placeholder: "",
29501
+ style: styles$j.singleSelectShort
29502
+ }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React.createElement(OptionItem, {
29490
29503
  key: n,
29491
- value: n
29492
- }, `${n} point${n > 1 ? "s" : ""}`)), /*#__PURE__*/React.createElement("option", {
29493
- value: UNLIMITED
29494
- }, "unlimited"));
29504
+ value: `${n}`,
29505
+ label: `${n} point${n > 1 ? "s" : ""}`
29506
+ })), /*#__PURE__*/React.createElement(OptionItem, {
29507
+ value: UNLIMITED,
29508
+ label: "unlimited"
29509
+ })]);
29495
29510
  };
29511
+ const styles$j = StyleSheet.create({
29512
+ singleSelectShort: {
29513
+ // Non-standard spacing, but it's the smallest we can go
29514
+ // without running into styling issues with the dropdown.
29515
+ height: 26
29516
+ }
29517
+ });
29496
29518
 
29497
29519
  const GraphTypeSelector = props => {
29498
- return /*#__PURE__*/React.createElement("select", {
29499
- value: props.graphType,
29500
- onChange: e => {
29501
- const type = e.target.value;
29502
- props.onChange(type);
29503
- }
29504
- }, /*#__PURE__*/React.createElement("option", {
29505
- value: "linear"
29506
- }, "Linear function"), /*#__PURE__*/React.createElement("option", {
29507
- value: "quadratic"
29508
- }, "Quadratic function"), /*#__PURE__*/React.createElement("option", {
29509
- value: "sinusoid"
29510
- }, "Sinusoid function"), /*#__PURE__*/React.createElement("option", {
29511
- value: "circle"
29512
- }, "Circle"), /*#__PURE__*/React.createElement("option", {
29513
- value: "point"
29514
- }, "Point(s)"), /*#__PURE__*/React.createElement("option", {
29515
- value: "linear-system"
29516
- }, "Linear System"), /*#__PURE__*/React.createElement("option", {
29517
- value: "polygon"
29518
- }, "Polygon"), /*#__PURE__*/React.createElement("option", {
29519
- value: "segment"
29520
- }, "Line Segment(s)"), /*#__PURE__*/React.createElement("option", {
29521
- value: "ray"
29522
- }, "Ray"), /*#__PURE__*/React.createElement("option", {
29523
- value: "angle"
29524
- }, "Angle"));
29520
+ return /*#__PURE__*/React.createElement(SingleSelect, {
29521
+ selectedValue: props.graphType,
29522
+ onChange: props.onChange,
29523
+ placeholder: "Select a graph type",
29524
+ style: styles$i.singleSelectShort
29525
+ }, /*#__PURE__*/React.createElement(OptionItem, {
29526
+ value: "linear",
29527
+ label: "Linear function"
29528
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29529
+ value: "quadratic",
29530
+ label: "Quadratic function"
29531
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29532
+ value: "sinusoid",
29533
+ label: "Sinusoid function"
29534
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29535
+ value: "circle",
29536
+ label: "Circle"
29537
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29538
+ value: "point",
29539
+ label: "Point(s)"
29540
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29541
+ value: "linear-system",
29542
+ label: "Linear System"
29543
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29544
+ value: "polygon",
29545
+ label: "Polygon"
29546
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29547
+ value: "segment",
29548
+ label: "Line Segment(s)"
29549
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29550
+ value: "ray",
29551
+ label: "Ray"
29552
+ }), /*#__PURE__*/React.createElement(OptionItem, {
29553
+ value: "angle",
29554
+ label: "Angle"
29555
+ }));
29556
+ };
29557
+ const styles$i = StyleSheet.create({
29558
+ singleSelectShort: {
29559
+ // Non-standard spacing, but it's the smallest we can go
29560
+ // without running into styling issues with the dropdown.
29561
+ height: 26
29562
+ }
29563
+ });
29564
+
29565
+ // Renders the given children in a View laid out horizontally.
29566
+ const LabeledRow = props => {
29567
+ const {
29568
+ children,
29569
+ label,
29570
+ labelSide = "left",
29571
+ style
29572
+ } = props;
29573
+ return /*#__PURE__*/React.createElement("label", {
29574
+ className: css(styles$h.label)
29575
+ }, /*#__PURE__*/React.createElement(View, {
29576
+ style: [styles$h.row, style]
29577
+ }, labelSide === "start" || /*#__PURE__*/React.createElement(LabelSmall, {
29578
+ style: styles$h.spaceEnd
29579
+ }, label), children, labelSide === "end" && /*#__PURE__*/React.createElement(LabelSmall, {
29580
+ style: styles$h.spaceStart
29581
+ }, label)));
29525
29582
  };
29583
+ const styles$h = StyleSheet.create({
29584
+ label: {
29585
+ width: "fit-content"
29586
+ },
29587
+ row: {
29588
+ flexDirection: "row",
29589
+ marginTop: spacing.xSmall_8,
29590
+ alignItems: "center",
29591
+ width: "fit-content"
29592
+ },
29593
+ spaceStart: {
29594
+ marginInlineStart: spacing.xSmall_8
29595
+ },
29596
+ spaceEnd: {
29597
+ marginInlineEnd: spacing.xSmall_8
29598
+ }
29599
+ });
29526
29600
 
29527
29601
  const {
29528
29602
  ButtonGroup: ButtonGroup$2,
@@ -29815,75 +29889,70 @@ class InteractiveGraphSettings extends React.Component {
29815
29889
  className: "perseus-widget-row"
29816
29890
  }, /*#__PURE__*/React.createElement("div", {
29817
29891
  className: "perseus-widget-left-col"
29818
- }, /*#__PURE__*/React.createElement("label", {
29819
- htmlFor: "labels-x"
29820
- }, "x Label"), /*#__PURE__*/React.createElement("input", {
29821
- id: "labels-x",
29892
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
29893
+ label: "x Label"
29894
+ }, /*#__PURE__*/React.createElement("input", {
29822
29895
  type: "text",
29823
29896
  className: "graph-settings-axis-label",
29824
29897
  ref: this.labelXRef,
29825
29898
  onChange: e => this.changeLabel(0, e),
29826
29899
  value: this.state.labelsTextbox[0] || ""
29827
- })), /*#__PURE__*/React.createElement("div", {
29900
+ }))), /*#__PURE__*/React.createElement("div", {
29828
29901
  className: "perseus-widget-right-col"
29829
- }, /*#__PURE__*/React.createElement("label", {
29830
- htmlFor: "labels-y"
29831
- }, "y Label"), /*#__PURE__*/React.createElement("input", {
29832
- id: "labels-y",
29902
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
29903
+ label: "y Label"
29904
+ }, /*#__PURE__*/React.createElement("input", {
29833
29905
  type: "text",
29834
29906
  className: "graph-settings-axis-label",
29835
29907
  ref: this.labelYRef,
29836
29908
  onChange: e => this.changeLabel(1, e),
29837
29909
  value: this.state.labelsTextbox[1] || ""
29838
- }))), /*#__PURE__*/React.createElement("div", {
29910
+ })))), /*#__PURE__*/React.createElement("div", {
29839
29911
  className: "perseus-widget-row"
29840
29912
  }, /*#__PURE__*/React.createElement("div", {
29841
29913
  className: "perseus-widget-left-col"
29842
- }, /*#__PURE__*/React.createElement("label", {
29843
- htmlFor: "range-x"
29844
- }, "x Range"), /*#__PURE__*/React.createElement(RangeInput$4, {
29845
- id: "range-x",
29914
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
29915
+ label: "x Range"
29916
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
29846
29917
  value: this.state.rangeTextbox[0],
29847
29918
  onChange: vals => this.changeRange(0, vals)
29848
- })), /*#__PURE__*/React.createElement("div", {
29919
+ }))), /*#__PURE__*/React.createElement("div", {
29849
29920
  className: "perseus-widget-right-col"
29850
- }, /*#__PURE__*/React.createElement("label", {
29851
- htmlFor: "range-y"
29852
- }, "y Range"), /*#__PURE__*/React.createElement(RangeInput$4, {
29853
- id: "range-y",
29921
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
29922
+ label: "y Range"
29923
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
29854
29924
  value: this.state.rangeTextbox[1],
29855
29925
  onChange: vals => this.changeRange(1, vals)
29856
- }))), /*#__PURE__*/React.createElement("div", {
29926
+ })))), /*#__PURE__*/React.createElement("div", {
29857
29927
  className: "perseus-widget-row"
29858
29928
  }, /*#__PURE__*/React.createElement("div", {
29859
29929
  className: "perseus-widget-left-col"
29860
- }, /*#__PURE__*/React.createElement("label", {
29861
- htmlFor: "tick-step"
29862
- }, "Tick Step"), /*#__PURE__*/React.createElement(RangeInput$4, {
29863
- id: "tick-step",
29930
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
29931
+ label: "Tick Step"
29932
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
29864
29933
  value: this.state.stepTextbox,
29865
29934
  onChange: this.changeStep
29866
- })), /*#__PURE__*/React.createElement("div", {
29935
+ }))), /*#__PURE__*/React.createElement("div", {
29867
29936
  className: "perseus-widget-right-col"
29868
- }, /*#__PURE__*/React.createElement("label", {
29869
- htmlFor: "grid-step"
29870
- }, "Grid Step"), /*#__PURE__*/React.createElement(RangeInput$4, {
29871
- id: "grid-step",
29937
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
29938
+ label: "Grid Step"
29939
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
29872
29940
  value: this.state.gridStepTextbox,
29873
29941
  onChange: this.changeGridStep
29874
- }))), /*#__PURE__*/React.createElement("div", {
29942
+ })))), /*#__PURE__*/React.createElement("div", {
29875
29943
  className: "perseus-widget-row"
29876
29944
  }, /*#__PURE__*/React.createElement("div", {
29877
29945
  className: "perseus-widget-left-col"
29878
- }, /*#__PURE__*/React.createElement("label", {
29879
- htmlFor: "snap-step"
29880
- }, "Snap Step"), /*#__PURE__*/React.createElement(RangeInput$4, {
29881
- id: "snap-step",
29946
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
29947
+ label: "Snap Step"
29948
+ }, /*#__PURE__*/React.createElement(RangeInput$4, {
29882
29949
  value: this.state.snapStepTextbox,
29883
29950
  onChange: this.changeSnapStep
29884
- }))), /*#__PURE__*/React.createElement("div", {
29951
+ })))), /*#__PURE__*/React.createElement("div", {
29885
29952
  className: "perseus-widget-row"
29886
- }, /*#__PURE__*/React.createElement("label", null, "Markings: "), /*#__PURE__*/React.createElement(ButtonGroup$2, {
29953
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
29954
+ label: "Markings:"
29955
+ }, /*#__PURE__*/React.createElement(ButtonGroup$2, {
29887
29956
  value: this.props.markings,
29888
29957
  allowEmpty: false,
29889
29958
  buttons: [{
@@ -29897,24 +29966,23 @@ class InteractiveGraphSettings extends React.Component {
29897
29966
  content: "None"
29898
29967
  }],
29899
29968
  onChange: this.change("markings")
29900
- })), /*#__PURE__*/React.createElement("div", {
29969
+ }))), /*#__PURE__*/React.createElement("div", {
29901
29970
  className: "perseus-widget-left-col"
29902
29971
  }, /*#__PURE__*/React.createElement(PropCheckBox$7, {
29903
29972
  label: "Show tooltips",
29904
29973
  showTooltips: this.props.showTooltips,
29905
29974
  onChange: this.change
29906
- }))), /*#__PURE__*/React.createElement("div", {
29907
- className: "image-settings"
29908
- }, /*#__PURE__*/React.createElement("div", null, "Background image:"), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", {
29909
- htmlFor: "bg-url"
29910
- }, "Url:"), /*#__PURE__*/React.createElement("input", {
29911
- id: "bg-url",
29975
+ }))), /*#__PURE__*/React.createElement(LabeledRow, {
29976
+ label: "Background image URL:",
29977
+ style: styles$g.resetSpaceTop
29978
+ }, /*#__PURE__*/React.createElement("input", {
29912
29979
  type: "text",
29913
- className: "graph-settings-background-url",
29980
+ className: css(styles$g.backgroundUrlInput),
29914
29981
  ref: this.bgUrlRef,
29915
29982
  value: this.state.backgroundImage.url || "",
29916
29983
  onChange: e => {
29917
- const image = _.clone(this.props.backgroundImage);
29984
+ // Shallow-copied clone
29985
+ const image = _extends({}, this.props.backgroundImage);
29918
29986
  image.url = e.target.value;
29919
29987
  this.setState({
29920
29988
  backgroundImage: image
@@ -29922,39 +29990,80 @@ class InteractiveGraphSettings extends React.Component {
29922
29990
  },
29923
29991
  onKeyPress: this.changeBackgroundUrl,
29924
29992
  onBlur: this.changeBackgroundUrl
29925
- }), /*#__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("div", {
29926
- className: "misc-settings"
29927
- }, /*#__PURE__*/React.createElement("div", {
29928
- className: "perseus-widget-row"
29929
- }, /*#__PURE__*/React.createElement("div", {
29930
- className: "perseus-widget-left-col"
29993
+ }), /*#__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, {
29994
+ style: styles$g.rulerSection
29995
+ }, /*#__PURE__*/React.createElement(View, {
29996
+ style: styles$g.checkboxRow
29931
29997
  }, /*#__PURE__*/React.createElement(PropCheckBox$7, {
29932
29998
  label: "Show ruler",
29933
29999
  showRuler: this.props.showRuler,
29934
- onChange: this.change
29935
- })), /*#__PURE__*/React.createElement("div", {
29936
- className: "perseus-widget-right-col"
29937
- }, /*#__PURE__*/React.createElement(PropCheckBox$7, {
30000
+ onChange: this.change,
30001
+ style: styles$g.resetSpaceTop
30002
+ }), /*#__PURE__*/React.createElement(PropCheckBox$7, {
29938
30003
  label: "Show protractor",
29939
30004
  showProtractor: this.props.showProtractor,
29940
- onChange: this.change
29941
- }))), this.props.showRuler && /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", null, " ", "Ruler label:", " ", /*#__PURE__*/React.createElement("select", {
29942
- onChange: this.changeRulerLabel,
29943
- value: this.props.rulerLabel
29944
- }, /*#__PURE__*/React.createElement("option", {
29945
- value: ""
29946
- }, "None"), /*#__PURE__*/React.createElement("optgroup", {
29947
- label: "Metric"
29948
- }, this.renderLabelChoices([["milimeters", "mm"], ["centimeters", "cm"], ["meters", "m"], ["kilometers", "km"]])), /*#__PURE__*/React.createElement("optgroup", {
29949
- label: "Imperial"
29950
- }, this.renderLabelChoices([["inches", "in"], ["feet", "ft"], ["yards", "yd"], ["miles", "mi"]]))))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", null, " ", "Ruler ticks:", " ", /*#__PURE__*/React.createElement("select", {
29951
- onChange: this.changeRulerTicks,
29952
- value: this.props.rulerTicks
29953
- }, _.map([1, 2, 4, 8, 10, 16], function (n) {
29954
- return /*#__PURE__*/React.createElement("option", {
29955
- key: `ruler-tick-${n}`,
29956
- value: n
29957
- }, n);
30005
+ onChange: this.change,
30006
+ style: styles$g.resetSpaceTop
30007
+ })), this.props.showRuler && /*#__PURE__*/React.createElement(View, {
30008
+ style: styles$g.spaceTop
30009
+ }, /*#__PURE__*/React.createElement(LabeledRow, {
30010
+ label: "Ruler label:",
30011
+ style: styles$g.resetSpaceTop
30012
+ }, /*#__PURE__*/React.createElement(SingleSelect, {
30013
+ id: "ruler-label-select",
30014
+ selectedValue: this.props.rulerLabel,
30015
+ onChange: newValue => {
30016
+ this.change({
30017
+ rulerLabel: newValue
30018
+ });
30019
+ },
30020
+ placeholder: "None",
30021
+ style: styles$g.singleSelectShort
30022
+ }, /*#__PURE__*/React.createElement(OptionItem, {
30023
+ value: "",
30024
+ label: "None",
30025
+ horizontalRule: "full-width"
30026
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30027
+ value: "mm",
30028
+ label: "Milimeters"
30029
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30030
+ value: "cm",
30031
+ label: "Centimeters"
30032
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30033
+ value: "m",
30034
+ label: "Meters"
30035
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30036
+ value: "km",
30037
+ label: "Kilometers",
30038
+ horizontalRule: "full-width"
30039
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30040
+ value: "in",
30041
+ label: "Inches"
30042
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30043
+ value: "ft",
30044
+ label: "Feet"
30045
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30046
+ value: "yd",
30047
+ label: "Yards"
30048
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30049
+ value: "mi",
30050
+ label: "Miles"
30051
+ }))), /*#__PURE__*/React.createElement(LabeledRow, {
30052
+ label: "Ruler ticks:"
30053
+ }, /*#__PURE__*/React.createElement(SingleSelect, {
30054
+ id: "ruler-ticks-select",
30055
+ selectedValue: `${this.props.rulerTicks}`,
30056
+ onChange: newValue => {
30057
+ this.change({
30058
+ rulerTicks: newValue
30059
+ });
30060
+ },
30061
+ placeholder: "10",
30062
+ style: styles$g.singleSelectShort
30063
+ }, [1, 2, 4, 8, 10, 16].map(value => /*#__PURE__*/React.createElement(OptionItem, {
30064
+ key: value,
30065
+ value: `${value}`,
30066
+ label: `${value}`
29958
30067
  })))))));
29959
30068
  }
29960
30069
  }
@@ -29974,21 +30083,371 @@ InteractiveGraphSettings.defaultProps = {
29974
30083
  rulerLabel: "",
29975
30084
  rulerTicks: 10
29976
30085
  };
30086
+ const styles$g = StyleSheet.create({
30087
+ resetSpaceTop: {
30088
+ marginTop: 0
30089
+ },
30090
+ spaceTop: {
30091
+ marginTop: spacing.xSmall_8
30092
+ },
30093
+ singleSelectShort: {
30094
+ // Non-standard spacing, but it's the smallest we can go
30095
+ // without running into styling issues with the dropdown.
30096
+ height: 26
30097
+ },
30098
+ backgroundUrlInput: {
30099
+ border: `1px solid ${color.offBlack32}`,
30100
+ borderRadius: spacing.xxxSmall_4,
30101
+ padding: spacing.xxxSmall_4
30102
+ },
30103
+ checkboxRow: {
30104
+ flexDirection: "row",
30105
+ alignItems: "center",
30106
+ justifyContent: "space-between"
30107
+ },
30108
+ rulerSection: {
30109
+ marginTop: spacing.xSmall_8,
30110
+ borderTop: `1px solid ${color.offBlack16}`,
30111
+ paddingTop: spacing.xSmall_8,
30112
+ paddingBottom: spacing.xSmall_8,
30113
+ borderBottom: `1px solid ${color.offBlack16}`
30114
+ }
30115
+ });
30116
+
30117
+ /**
30118
+ * Dropdown for selecting a locked figure to add to an interactive graph.
30119
+ * Locked figures are elements (points, segmeents, etc.) that are not
30120
+ * interactive, just present in the graph's background.
30121
+ *
30122
+ * Used in the interactive graph editor's locked figures section.
30123
+ */
30124
+ const LockedFigureSelect = props => {
30125
+ const {
30126
+ id,
30127
+ onChange
30128
+ } = props;
30129
+ return /*#__PURE__*/React.createElement(View, {
30130
+ style: styles$f.container
30131
+ }, /*#__PURE__*/React.createElement(ActionMenu, {
30132
+ menuText: "Add element",
30133
+ style: styles$f.addElementSelect
30134
+ }, [/*#__PURE__*/React.createElement(ActionItem, {
30135
+ key: `${id}-point`,
30136
+ label: "Point",
30137
+ onClick: () => onChange("point")
30138
+ }, "Point")]));
30139
+ };
30140
+ const styles$f = StyleSheet.create({
30141
+ container: {
30142
+ marginTop: spacing.medium_16
30143
+ },
30144
+ addElementSelect: {
30145
+ backgroundColor: color.fadedBlue16,
30146
+ borderRadius: spacing.xxxSmall_4
30147
+ }
30148
+ });
30149
+
30150
+ const mediaDefaultSpecLargeMarginWidth = spacing.large_24;
30151
+ const MEDIA_DEFAULT_SPEC = {
30152
+ small: {
30153
+ query: "(max-width: 767px)",
30154
+ totalColumns: 4,
30155
+ gutterWidth: spacing.medium_16,
30156
+ marginWidth: spacing.medium_16
30157
+ },
30158
+ medium: {
30159
+ query: "(min-width: 768px) and (max-width: 1023px)",
30160
+ totalColumns: 8,
30161
+ gutterWidth: spacing.xLarge_32,
30162
+ marginWidth: spacing.large_24
30163
+ },
30164
+ large: {
30165
+ query: "(min-width: 1024px)",
30166
+ totalColumns: 12,
30167
+ gutterWidth: spacing.xLarge_32,
30168
+ marginWidth: mediaDefaultSpecLargeMarginWidth,
30169
+ maxWidth: 1120 + mediaDefaultSpecLargeMarginWidth * 2
30170
+ }
30171
+ };
30172
+ ({
30173
+ large: {
30174
+ query: "(min-width: 1px)",
30175
+ totalColumns: 12,
30176
+ gutterWidth: spacing.xLarge_32,
30177
+ marginWidth: spacing.medium_16
30178
+ }
30179
+ });
30180
+ ({
30181
+ small: {
30182
+ query: "(max-width: 767px)",
30183
+ totalColumns: 4,
30184
+ gutterWidth: spacing.medium_16,
30185
+ marginWidth: spacing.medium_16
30186
+ },
30187
+ large: {
30188
+ query: "(min-width: 768px)",
30189
+ totalColumns: 12,
30190
+ gutterWidth: spacing.xLarge_32,
30191
+ marginWidth: spacing.xxLarge_48
30192
+ }
30193
+ });
30194
+
30195
+ const defaultContext = {
30196
+ ssrSize: "large",
30197
+ mediaSpec: MEDIA_DEFAULT_SPEC
30198
+ };
30199
+ const MediaLayoutContext = React.createContext(defaultContext);
30200
+ MediaLayoutContext.displayName = "MediaLayoutContext";
30201
+
30202
+ class Spring extends React.Component {
30203
+ render() {
30204
+ const {
30205
+ style
30206
+ } = this.props;
30207
+ return React.createElement(View, {
30208
+ "aria-hidden": "true",
30209
+ style: [styles$e.grow, style]
30210
+ });
30211
+ }
30212
+ }
30213
+ const styles$e = StyleSheet.create({
30214
+ grow: {
30215
+ flexGrow: 1
30216
+ }
30217
+ });
30218
+
30219
+ function focusWithChromeStickyFocusBugWorkaround(element) {
30220
+ // NOTE(marksandstrom) Chrome sticky focus bug.
30221
+ //
30222
+ // There is a bug in Chrome related to sticky positioning and
30223
+ // focus: When a child of an element with sticky positioning is
30224
+ // focused with the focus() method, the scrollable area
30225
+ // containing that element will scroll to the top (even though
30226
+ // the element is already visble on the page). For a dropdown
30227
+ // this means that the area will be scrolled to the top when
30228
+ // the user interacts with the dropdown (since we actively
30229
+ // manage dropdown header and option focus for keyboard users).
30230
+ //
30231
+ // To work around this bug the `preventScroll` focus option is
30232
+ // used.
30233
+ //
30234
+ // Using `focusOptions` is an experimental browser feature
30235
+ // "that should not be used in production code". However, since
30236
+ // Chrome is the only browser we support that exhibits this
30237
+ // buggy behavior and using `focusOptions` works around the
30238
+ // bug, it is perfectly reasonable to use the feature in this
30239
+ // case. We are not relying on the effect of the
30240
+ // `preventScroll` option otherwise.
30241
+ //
30242
+ // focusOptions documentation:
30243
+ //
30244
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
30245
+ //
30246
+ // Note that there could be cases where we want default focus() behavior.
30247
+ // One that comes to mind is a dropdown with a long list of options that
30248
+ // might be rendered off screen when opened. With this workaround the
30249
+ // selected option would not be scrolled into view if the option is near
30250
+ // the end of the list. We're not aware of any uses of the dropdown where
30251
+ // this is an issue, however, so there isn't currently a way to out of the
30252
+ // `preventScroll` behavior.
30253
+
30254
+ // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element'.
30255
+ element.focus({
30256
+ preventScroll: true
30257
+ });
30258
+ }
30259
+ function getValidNumberFromString(value) {
30260
+ const parsed = parseInt(value);
30261
+ // If the value is not a number, return 0.
30262
+ return isNaN(parsed) ? 0 : parsed;
30263
+ }
30264
+ function getDefaultFigureForFigureType(type) {
30265
+ switch (type) {
30266
+ case "point":
30267
+ return {
30268
+ type: "point",
30269
+ coord: [0, 0]
30270
+ };
30271
+ }
30272
+ }
30273
+
30274
+ /**
30275
+ * LockedPointSettings is a component that allows the user to edit the
30276
+ * settings of specifically a locked point on the graph.
30277
+ *
30278
+ * Used in the interactive graph editor's locked figures section.
30279
+ */
30280
+ const LockedPointSettings = props => {
30281
+ const {
30282
+ coord,
30283
+ onRemove,
30284
+ onChangeCoord
30285
+ } = props;
30286
+ const [coordState, setCoordState] = React.useState([
30287
+ // Using strings to make it easier to work with the text fields.
30288
+ coord[0].toString(), coord[1].toString()]);
30289
+
30290
+ // Generate unique IDs so that the programmatic labels can be associated
30291
+ // with their respective text fields.
30292
+ const ids = useUniqueIdWithMock();
30293
+ const xCoordId = ids.get("x-coord");
30294
+ const yCoordId = ids.get("y-coord");
30295
+ function handleBlur() {
30296
+ const validCoord = [getValidNumberFromString(coordState[0]), getValidNumberFromString(coordState[1])];
30297
+
30298
+ // Make the text field only show valid numbers after blur.
30299
+ setCoordState([validCoord[0].toString(), validCoord[1].toString()]);
30300
+ // Update the graph with the new coordinates.
30301
+ onChangeCoord(validCoord);
30302
+ }
30303
+ function handleChange(newValue, coordIndex) {
30304
+ const newCoord = [...coordState];
30305
+ newCoord[coordIndex] = newValue;
30306
+ setCoordState(newCoord);
30307
+ }
30308
+ return /*#__PURE__*/React.createElement(View, {
30309
+ style: styles$d.container
30310
+ }, /*#__PURE__*/React.createElement(View, {
30311
+ style: styles$d.row
30312
+ }, /*#__PURE__*/React.createElement(LabelLarge, null, "Point"), /*#__PURE__*/React.createElement(Spring, null), /*#__PURE__*/React.createElement(IconButton, {
30313
+ icon: trashIcon,
30314
+ "aria-label": `Delete locked point at ${coordState[0]}, ${coordState[1]}`,
30315
+ onClick: onRemove
30316
+ })), /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(View, {
30317
+ style: styles$d.row
30318
+ }, /*#__PURE__*/React.createElement(LabelMedium, {
30319
+ htmlFor: xCoordId,
30320
+ style: styles$d.label,
30321
+ tag: "label"
30322
+ }, "x Coordinate"), /*#__PURE__*/React.createElement(TextField, {
30323
+ id: xCoordId,
30324
+ value: coordState[0],
30325
+ onChange: newValue => handleChange(newValue, 0),
30326
+ onBlur: handleBlur,
30327
+ style: styles$d.textField
30328
+ })), /*#__PURE__*/React.createElement(View, {
30329
+ style: styles$d.row
30330
+ }, /*#__PURE__*/React.createElement(LabelMedium, {
30331
+ htmlFor: yCoordId,
30332
+ style: styles$d.label,
30333
+ tag: "label"
30334
+ }, "y Coordinate"), /*#__PURE__*/React.createElement(TextField, {
30335
+ id: yCoordId,
30336
+ value: coordState[1],
30337
+ onChange: newValue => handleChange(newValue, 1),
30338
+ onBlur: handleBlur,
30339
+ style: styles$d.textField
30340
+ }))));
30341
+ };
30342
+ const styles$d = StyleSheet.create({
30343
+ container: {
30344
+ backgroundColor: color.fadedBlue8,
30345
+ marginBottom: spacing.xSmall_8,
30346
+ padding: spacing.medium_16,
30347
+ borderRadius: spacing.xSmall_8
30348
+ },
30349
+ row: {
30350
+ flexDirection: "row",
30351
+ alignItems: "center",
30352
+ marginBottom: spacing.xSmall_8
30353
+ },
30354
+ label: {
30355
+ marginInlineEnd: spacing.xSmall_8
30356
+ },
30357
+ textField: {
30358
+ width: spacing.xxxLarge_64
30359
+ }
30360
+ });
30361
+
30362
+ /**
30363
+ * LockedFigureSettings takes in a figure and renders the
30364
+ * respective settings for that figure type.
30365
+ *
30366
+ * Used in the interactive graph editor's locked figures section.
30367
+ */
30368
+
30369
+ // Union this type with other locked figure types when they are added.
30370
+
30371
+ const LockedFigureSettings = props => {
30372
+ switch (props.type) {
30373
+ case "point":
30374
+ return /*#__PURE__*/React.createElement(LockedPointSettings, props);
30375
+ }
30376
+ return null;
30377
+ };
30378
+
30379
+ const LockedFiguresSection = props => {
30380
+ const uniqueId = useUniqueIdWithMock().get("locked-figures-section");
30381
+ const {
30382
+ figures,
30383
+ onChange
30384
+ } = props;
30385
+ function addLockedFigure(newFigure) {
30386
+ const lockedFigures = figures || [];
30387
+ const newProps = {
30388
+ lockedFigures: [...lockedFigures, getDefaultFigureForFigureType(newFigure)]
30389
+ };
30390
+ onChange(newProps);
30391
+ }
30392
+ function removeLockedFigure(index) {
30393
+ const lockedFigures = figures || [];
30394
+ onChange({
30395
+ lockedFigures: [...lockedFigures.slice(0, index), ...lockedFigures.slice(index + 1)]
30396
+ });
30397
+ }
30398
+ function changeCoord(index, coord) {
30399
+ const lockedFigures = figures || [];
30400
+ const newProps = {
30401
+ lockedFigures: [...lockedFigures.slice(0, index), _extends({}, lockedFigures[index], {
30402
+ coord
30403
+ }), ...lockedFigures.slice(index + 1)]
30404
+ };
30405
+ onChange(newProps);
30406
+ }
30407
+ return /*#__PURE__*/React.createElement(View, {
30408
+ style: styles$c.container
30409
+ }, figures == null ? void 0 : figures.map((figure, index) => /*#__PURE__*/React.createElement(LockedFigureSettings, _extends({
30410
+ key: `${uniqueId}-locked-${figure}-${index}`
30411
+ }, figure, {
30412
+ onRemove: () => removeLockedFigure(index),
30413
+ onChangeCoord: coord => changeCoord(index, coord)
30414
+ }))), /*#__PURE__*/React.createElement(LockedFigureSelect, {
30415
+ id: `${uniqueId}-select`,
30416
+ onChange: addLockedFigure
30417
+ }));
30418
+ };
30419
+ const styles$c = StyleSheet.create({
30420
+ container: {
30421
+ paddingTop: spacing.large_24
30422
+ }
30423
+ });
29977
30424
 
29978
30425
  const SegmentCountSelector = ({
29979
30426
  numSegments: _numSegments = 1,
29980
30427
  onChange
29981
- }) => /*#__PURE__*/React.createElement("select", {
30428
+ }) => /*#__PURE__*/React.createElement(SingleSelect, {
29982
30429
  key: "segment-select",
29983
- value: _numSegments,
29984
- onChange: e => {
29985
- const num = +e.target.value;
30430
+ selectedValue: `${_numSegments}`
30431
+ // Never uses placeholder, always has value
30432
+ ,
30433
+ placeholder: "",
30434
+ onChange: newValue => {
30435
+ const num = +newValue;
29986
30436
  onChange(num);
29987
- }
29988
- }, _.range(1, 7).map(n => /*#__PURE__*/React.createElement("option", {
30437
+ },
30438
+ style: styles$b.singleSelectShort
30439
+ }, _.range(1, 7).map(n => /*#__PURE__*/React.createElement(OptionItem, {
29989
30440
  key: n,
29990
- value: n
29991
- }, `${n} segment${n > 1 ? "s" : ""}`)));
30441
+ value: `${n}`,
30442
+ label: `${n} segment${n > 1 ? "s" : ""}`
30443
+ })));
30444
+ const styles$b = StyleSheet.create({
30445
+ singleSelectShort: {
30446
+ // Non-standard spacing, but it's the smallest we can go
30447
+ // without running into styling issues with the dropdown.
30448
+ height: 26
30449
+ }
30450
+ });
29992
30451
 
29993
30452
  const {
29994
30453
  InfoTip: InfoTip$b
@@ -30009,26 +30468,13 @@ const deprecatedProps = {
30009
30468
  };
30010
30469
  }
30011
30470
  };
30012
- // Renders the given children in a View laid out horizontally.
30013
- const Row = props => {
30014
- const {
30015
- children,
30016
- style
30017
- } = props;
30018
- return /*#__PURE__*/React.createElement(View, {
30019
- style: _extends({}, style, {
30020
- flexDirection: "row",
30021
- marginTop: Spacing.xSmall_8
30022
- })
30023
- }, children);
30024
- };
30025
- const FieldLabel = props => {
30026
- return /*#__PURE__*/React.createElement(Typography.LabelSmall, {
30027
- style: {
30028
- marginRight: Spacing.xSmall_8
30029
- }
30030
- }, props.children);
30031
- };
30471
+ const POLYGON_SIDES = _.map(_.range(3, 13), function (value) {
30472
+ return /*#__PURE__*/React.createElement(OptionItem, {
30473
+ key: `polygon-sides-${value}`,
30474
+ value: `${value}`,
30475
+ label: `${value} sides`
30476
+ });
30477
+ });
30032
30478
  /**
30033
30479
  * An editor for the InteractiveGraph widget, which allows the user to
30034
30480
  * specify the graph's properties and the correct answer.
@@ -30042,13 +30488,21 @@ class InteractiveGraphEditor extends React.Component {
30042
30488
  this.className = "perseus-widget-interactive-graph";
30043
30489
  // TODO(jack): Use versioning instead of DeprecationMixin
30044
30490
  this.deprecatedProps = deprecatedProps;
30491
+ this.changeMatchType = newValue => {
30492
+ const correct = _extends({}, this.props.correct, {
30493
+ match: newValue
30494
+ });
30495
+ this.props.onChange({
30496
+ correct: correct
30497
+ });
30498
+ };
30045
30499
  }
30046
30500
  // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
30047
30501
  UNSAFE_componentWillMount() {
30048
30502
  DeprecationMixin.UNSAFE_componentWillMount.call(this);
30049
30503
  }
30050
30504
  render() {
30051
- 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;
30505
+ 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;
30052
30506
  let graph;
30053
30507
  let equationString;
30054
30508
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, interactiveSizes.defaultBoxSize);
@@ -30072,6 +30526,7 @@ class InteractiveGraphEditor extends React.Component {
30072
30526
  showTooltips: this.props.showTooltips,
30073
30527
  rulerLabel: this.props.rulerLabel,
30074
30528
  rulerTicks: this.props.rulerTicks,
30529
+ lockedFigures: this.props.lockedFigures,
30075
30530
  trackInteraction: function () {},
30076
30531
  onChange: newProps => {
30077
30532
  let correct = this.props.correct;
@@ -30108,7 +30563,9 @@ class InteractiveGraphEditor extends React.Component {
30108
30563
  className: "perseus-error"
30109
30564
  }, this.props.valid);
30110
30565
  }
30111
- return /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Type of Graph:"), /*#__PURE__*/React.createElement(GraphTypeSelector, {
30566
+ return /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(LabeledRow, {
30567
+ label: "Type of Graph:"
30568
+ }, /*#__PURE__*/React.createElement(GraphTypeSelector, {
30112
30569
  graphType: (_this$props$graph$typ = (_this$props$graph = this.props.graph) == null ? void 0 : _this$props$graph.type) != null ? _this$props$graph$typ : InteractiveGraph.defaultProps.graph.type,
30113
30570
  onChange: type => {
30114
30571
  this.props.onChange({
@@ -30120,7 +30577,9 @@ class InteractiveGraphEditor extends React.Component {
30120
30577
  }
30121
30578
  });
30122
30579
  }
30123
- })), ((_this$props$correct = this.props.correct) == null ? void 0 : _this$props$correct.type) === "point" && /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Number of Points:"), /*#__PURE__*/React.createElement(GraphPointsCountSelector, {
30580
+ })), ((_this$props$correct = this.props.correct) == null ? void 0 : _this$props$correct.type) === "point" && /*#__PURE__*/React.createElement(LabeledRow, {
30581
+ label: "Number of Points:"
30582
+ }, /*#__PURE__*/React.createElement(GraphPointsCountSelector, {
30124
30583
  numPoints: (_this$props$correct2 = this.props.correct) == null ? void 0 : _this$props$correct2.numPoints,
30125
30584
  onChange: points => {
30126
30585
  this.props.onChange({
@@ -30130,12 +30589,15 @@ class InteractiveGraphEditor extends React.Component {
30130
30589
  }
30131
30590
  });
30132
30591
  }
30133
- })), ((_this$props$correct3 = this.props.correct) == null ? void 0 : _this$props$correct3.type) === "polygon" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Number of sides:"), /*#__PURE__*/React.createElement("select", {
30592
+ })), ((_this$props$correct3 = this.props.correct) == null ? void 0 : _this$props$correct3.type) === "polygon" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LabeledRow, {
30593
+ label: "Number of sides:"
30594
+ }, /*#__PURE__*/React.createElement(SingleSelect, {
30134
30595
  key: "polygon-select",
30135
- value: ((_this$props$correct4 = this.props.correct) == null ? void 0 : _this$props$correct4.numSides) || 3,
30136
- onChange: e => {
30596
+ selectedValue: (_this$props$correct4 = this.props.correct) != null && _this$props$correct4.numSides ? `${this.props.correct.numSides}` : "3",
30597
+ placeholder: "",
30598
+ onChange: newValue => {
30137
30599
  const graph = _extends({}, this.props.correct, {
30138
- numSides: parsePointCount(e.target.value),
30600
+ numSides: parsePointCount(newValue),
30139
30601
  coords: null,
30140
30602
  // reset the snap for UNLIMITED, which
30141
30603
  // only supports "grid"
@@ -30145,37 +30607,44 @@ class InteractiveGraphEditor extends React.Component {
30145
30607
  this.props.onChange({
30146
30608
  correct: graph
30147
30609
  });
30148
- }
30149
- }, _.map(_.range(3, 13), function (n) {
30150
- return /*#__PURE__*/React.createElement("option", {
30151
- key: n,
30152
- value: n
30153
- }, `${n} sides`);
30154
- }), /*#__PURE__*/React.createElement("option", {
30155
- value: "unlimited"
30156
- }, "unlimited sides"))), /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Snap to:"), /*#__PURE__*/React.createElement("select", {
30157
- key: "polygon-snap",
30158
- value: (_this$props$correct5 = this.props.correct) == null ? void 0 : _this$props$correct5.snapTo,
30159
- onChange: e => {
30610
+ },
30611
+ style: styles$a.singleSelectShort
30612
+ }, [...POLYGON_SIDES, /*#__PURE__*/React.createElement(OptionItem, {
30613
+ value: "unlimited",
30614
+ label: "unlimited sides"
30615
+ })])), /*#__PURE__*/React.createElement(LabeledRow, {
30616
+ label: "Snap to:"
30617
+ }, /*#__PURE__*/React.createElement(SingleSelect, {
30618
+ selectedValue: ((_this$props$correct5 = this.props.correct) == null ? void 0 : _this$props$correct5.snapTo) || "grid"
30619
+ // Never uses placeholder, always has value
30620
+ ,
30621
+ placeholder: "",
30622
+ onChange: newValue => {
30160
30623
  const graph = _extends({}, this.props.correct, {
30161
- snapTo: e.target.value,
30624
+ snapTo: newValue,
30162
30625
  coords: null
30163
30626
  });
30164
30627
  this.props.onChange({
30165
30628
  correct: graph
30166
30629
  });
30167
- }
30168
- }, /*#__PURE__*/React.createElement("option", {
30169
- value: "grid"
30170
- }, "grid"), ((_this$props$correct6 = this.props.correct) == null ? void 0 : _this$props$correct6.numSides) !== "unlimited" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("option", {
30171
- value: "angles"
30172
- }, "interior angles"), /*#__PURE__*/React.createElement("option", {
30173
- value: "sides"
30174
- }, "side measures"))), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "These options affect the movement of the vertex points. The grid option will guide the points to the nearest half step along the grid."), /*#__PURE__*/React.createElement("p", null, "The interior angle and side measure options guide the points to the nearest whole angle or side"), " ", "measure respectively.")), /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Typography.LabelSmall, {
30175
- tag: "label"
30176
- }, "Show angle measures:", " ", /*#__PURE__*/React.createElement("input", {
30177
- type: "checkbox",
30178
- checked: (_this$props$correct7 = this.props.correct) == null ? void 0 : _this$props$correct7.showAngles,
30630
+ },
30631
+ style: styles$a.singleSelectShort
30632
+ }, /*#__PURE__*/React.createElement(OptionItem, {
30633
+ value: "grid",
30634
+ label: "grid"
30635
+ }), ((_this$props$correct6 = this.props.correct) == null ? void 0 : _this$props$correct6.numSides) !== "unlimited" && /*#__PURE__*/React.createElement(OptionItem, {
30636
+ value: "angles",
30637
+ label: "interior angles"
30638
+ }), ((_this$props$correct7 = this.props.correct) == null ? void 0 : _this$props$correct7.numSides) !== "unlimited" && /*#__PURE__*/React.createElement(OptionItem, {
30639
+ value: "sides",
30640
+ label: "side measures"
30641
+ })), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "These options affect the movement of the vertex points. The grid option will guide the points to the nearest half step along the grid."), /*#__PURE__*/React.createElement("p", null, "The interior angle and side measure options guide the points to the nearest whole angle or side"), " ", "measure respectively.")), /*#__PURE__*/React.createElement(View, {
30642
+ style: styles$a.row
30643
+ }, /*#__PURE__*/React.createElement(Checkbox$1, {
30644
+ label: /*#__PURE__*/React.createElement(LabelSmall, null, "Show angle measures"),
30645
+ checked:
30646
+ // Don't show indeterminate checkbox state
30647
+ !!((_this$props$correct8 = this.props.correct) != null && _this$props$correct8.showAngles),
30179
30648
  onChange: () => {
30180
30649
  this.props.onChange({
30181
30650
  correct: _extends({}, this.props.correct, {
@@ -30183,11 +30652,13 @@ class InteractiveGraphEditor extends React.Component {
30183
30652
  })
30184
30653
  });
30185
30654
  }
30186
- })), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "Displays the interior angle measures."))), /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Typography.LabelSmall, {
30187
- tag: "label"
30188
- }, "Show side measures:", " ", /*#__PURE__*/React.createElement("input", {
30189
- type: "checkbox",
30190
- checked: (_this$props$correct8 = this.props.correct) == null ? void 0 : _this$props$correct8.showSides,
30655
+ }), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "Displays the interior angle measures."))), /*#__PURE__*/React.createElement(View, {
30656
+ style: styles$a.row
30657
+ }, /*#__PURE__*/React.createElement(Checkbox$1, {
30658
+ label: /*#__PURE__*/React.createElement(LabelSmall, null, "Show side measures"),
30659
+ checked:
30660
+ // Don't show indeterminate checkbox state
30661
+ !!((_this$props$correct9 = this.props.correct) != null && _this$props$correct9.showSides),
30191
30662
  onChange: () => {
30192
30663
  this.props.onChange({
30193
30664
  correct: _extends({}, this.props.correct, {
@@ -30195,8 +30666,10 @@ class InteractiveGraphEditor extends React.Component {
30195
30666
  })
30196
30667
  });
30197
30668
  }
30198
- })), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "Displays the side lengths.")))), ((_this$props$correct9 = this.props.correct) == null ? void 0 : _this$props$correct9.type) === "segment" && /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Number of segments: "), /*#__PURE__*/React.createElement(SegmentCountSelector, {
30199
- numSegments: (_this$props$correct10 = this.props.correct) == null ? void 0 : _this$props$correct10.numSegments,
30669
+ }), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "Displays the side lengths.")))), ((_this$props$correct10 = this.props.correct) == null ? void 0 : _this$props$correct10.type) === "segment" && /*#__PURE__*/React.createElement(LabeledRow, {
30670
+ label: "Number of segments:"
30671
+ }, /*#__PURE__*/React.createElement(SegmentCountSelector, {
30672
+ numSegments: (_this$props$correct11 = this.props.correct) == null ? void 0 : _this$props$correct11.numSegments,
30200
30673
  onChange: sides => {
30201
30674
  this.props.onChange({
30202
30675
  correct: {
@@ -30206,16 +30679,7 @@ class InteractiveGraphEditor extends React.Component {
30206
30679
  }
30207
30680
  });
30208
30681
  }
30209
- })), /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Correct answer:"), /*#__PURE__*/React.createElement(Typography.BodyMonospace, {
30210
- style: {
30211
- fontSize: 12,
30212
- backgroundColor: "#eee",
30213
- paddingInline: Spacing.xxSmall_6,
30214
- borderColor: "#ccc",
30215
- borderStyle: "solid",
30216
- borderWidth: 1
30217
- }
30218
- }, 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."))), /*#__PURE__*/React.createElement(InteractiveGraphSettings, {
30682
+ })), /*#__PURE__*/React.createElement(InteractiveGraphSettings, {
30219
30683
  box: getInteractiveBoxFromSizeClass(sizeClass),
30220
30684
  range: this.props.range,
30221
30685
  labels: this.props.labels,
@@ -30231,40 +30695,63 @@ class InteractiveGraphEditor extends React.Component {
30231
30695
  rulerLabel: this.props.rulerLabel,
30232
30696
  rulerTicks: this.props.rulerTicks,
30233
30697
  onChange: this.props.onChange
30234
- }), this.props.correct.type === "polygon" && /*#__PURE__*/React.createElement("div", {
30235
- className: "type-settings"
30236
- }, /*#__PURE__*/React.createElement("label", null, " ", "Student answer must", " ", /*#__PURE__*/React.createElement("select", {
30237
- value: this.props.correct.match,
30698
+ }), this.props.correct.type === "polygon" && /*#__PURE__*/React.createElement(LabeledRow, {
30699
+ label: "Student answer must"
30700
+ }, /*#__PURE__*/React.createElement(SingleSelect, {
30701
+ selectedValue: this.props.correct.match || "exact",
30238
30702
  onChange: this.changeMatchType
30239
- }, /*#__PURE__*/React.createElement("option", {
30240
- value: "exact"
30241
- }, "match exactly"), /*#__PURE__*/React.createElement("option", {
30242
- value: "congruent"
30243
- }, "be congruent"), /*#__PURE__*/React.createElement("option", {
30244
- value: "approx"
30245
- }, "be approximately congruent"), /*#__PURE__*/React.createElement("option", {
30246
- value: "similar"
30247
- }, "be similar"))), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("ul", null, /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, "Match Exactly:"), " Match exactly in size, orientation, and location on the grid even if it is not shown in the background.")), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, "Be Congruent:"), " Be congruent in size and shape, but can be located anywhere on the grid.")), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, "Be Approximately Congruent:"), " Be exactly similar, and congruent in size and shape to within 0.1 units, but can be located anywhere on the grid.", " ", /*#__PURE__*/React.createElement("em", null, "Use this with snapping to angle measure."))), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, "Be Similar:"), " Be similar with matching interior angles, and side measures that are matching or a multiple of the correct side measures. The figure can be located anywhere on the grid."))))), this.props.correct.type === "angle" && /*#__PURE__*/React.createElement("div", {
30248
- className: "type-settings"
30249
- }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", null, " ", "Student answer must", " ", /*#__PURE__*/React.createElement("select", {
30250
- value: this.props.correct.match,
30703
+ // Never uses placeholder, always has value
30704
+ ,
30705
+ placeholder: "",
30706
+ style: styles$a.singleSelectShort
30707
+ }, /*#__PURE__*/React.createElement(OptionItem, {
30708
+ value: "exact",
30709
+ label: "match exactly"
30710
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30711
+ value: "congruent",
30712
+ label: "be congruent"
30713
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30714
+ value: "approx",
30715
+ label: "be approximately congruent"
30716
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30717
+ value: "similar",
30718
+ label: "be similar"
30719
+ })), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("ul", null, /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, "Match Exactly:"), " Match exactly in size, orientation, and location on the grid even if it is not shown in the background.")), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, "Be Congruent:"), " Be congruent in size and shape, but can be located anywhere on the grid.")), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, "Be Approximately Congruent:"), " Be exactly similar, and congruent in size and shape to within 0.1 units, but can be located anywhere on the grid.", " ", /*#__PURE__*/React.createElement("em", null, "Use this with snapping to angle measure."))), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, "Be Similar:"), " Be similar with matching interior angles, and side measures that are matching or a multiple of the correct side measures. The figure can be located anywhere on the grid."))))), this.props.correct.type === "angle" && /*#__PURE__*/React.createElement(LabeledRow, {
30720
+ label: "Student answer must"
30721
+ }, /*#__PURE__*/React.createElement(SingleSelect, {
30722
+ selectedValue: this.props.correct.match || "exact",
30251
30723
  onChange: this.changeMatchType
30252
- }, /*#__PURE__*/React.createElement("option", {
30253
- value: "exact"
30254
- }, "match exactly"), /*#__PURE__*/React.createElement("option", {
30255
- value: "congruent"
30256
- }, "be congruent"))), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "Congruency requires only that the angle measures are the same. An exact match implies congruency, but also requires that the angles have the same orientation and that the vertices are in the same position.")))), graph);
30257
- }
30258
- changeMatchType(e) {
30259
- const correct = _.extend({}, this.props.correct, {
30260
- match: e.target.value
30261
- });
30262
- this.props.onChange({
30263
- correct: correct
30264
- });
30724
+ // Never uses placeholder, always has value
30725
+ ,
30726
+ placeholder: "",
30727
+ style: styles$a.singleSelectShort
30728
+ }, /*#__PURE__*/React.createElement(OptionItem, {
30729
+ value: "exact",
30730
+ label: "match exactly"
30731
+ }), /*#__PURE__*/React.createElement(OptionItem, {
30732
+ value: "congruent",
30733
+ label: "be congruent"
30734
+ })), /*#__PURE__*/React.createElement(InfoTip$b, null, /*#__PURE__*/React.createElement("p", null, "Congruency requires only that the angle measures are the same. An exact match implies congruency, but also requires that the angles have the same orientation and that the vertices are in the same position."))), /*#__PURE__*/React.createElement(LabeledRow, {
30735
+ label: "Correct answer:"
30736
+ }, /*#__PURE__*/React.createElement(BodyMonospace, {
30737
+ style: {
30738
+ fontSize: 12,
30739
+ backgroundColor: "#eee",
30740
+ paddingInline: Spacing.xxSmall_6,
30741
+ borderColor: "#ccc",
30742
+ borderStyle: "solid",
30743
+ borderWidth: 1
30744
+ }
30745
+ }, 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,
30746
+ // Only show the "Add element" dropdown if the graph is
30747
+ // using Mafs.
30748
+ 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, {
30749
+ figures: this.props.lockedFigures,
30750
+ onChange: this.props.onChange
30751
+ }));
30265
30752
  }
30266
30753
  serialize() {
30267
- const json = _.pick(this.props, "step", "backgroundImage", "markings", "labels", "showProtractor", "showRuler", "showTooltips", "rulerLabel", "rulerTicks", "range", "gridStep", "snapStep");
30754
+ const json = _.pick(this.props, "step", "backgroundImage", "markings", "labels", "showProtractor", "showRuler", "showTooltips", "rulerLabel", "rulerTicks", "range", "gridStep", "snapStep", "lockedFigures");
30268
30755
 
30269
30756
  // eslint-disable-next-line react/no-string-refs
30270
30757
  const graph = this.refs.graph;
@@ -30298,6 +30785,18 @@ InteractiveGraphEditor.defaultProps = _extends({}, InteractiveGraph.defaultProps
30298
30785
  coords: null
30299
30786
  }
30300
30787
  });
30788
+ const styles$a = StyleSheet.create({
30789
+ singleSelectShort: {
30790
+ // Non-standard spacing, but it's the smallest we can go
30791
+ // without running into styling issues with the dropdown.
30792
+ height: 26
30793
+ },
30794
+ row: {
30795
+ flexDirection: "row",
30796
+ marginTop: Spacing.xSmall_8,
30797
+ alignItems: "center"
30798
+ }
30799
+ });
30301
30800
 
30302
30801
  const _excluded$1 = ["forwardedRef", "width", "grow", "shrink", "icon", "backgroundColor", "focusBorderColor", "borderColor", "color", "onSubmit", "leftSideIcon", "id", "testId", "type", "labelMediumInputText"];
30303
30802
  class FormWrappedTextField extends React.Component {
@@ -31012,47 +31511,6 @@ const styles$5 = StyleSheet.create({
31012
31511
  })
31013
31512
  });
31014
31513
 
31015
- function focusWithChromeStickyFocusBugWorkaround(element) {
31016
- // NOTE(marksandstrom) Chrome sticky focus bug.
31017
- //
31018
- // There is a bug in Chrome related to sticky positioning and
31019
- // focus: When a child of an element with sticky positioning is
31020
- // focused with the focus() method, the scrollable area
31021
- // containing that element will scroll to the top (even though
31022
- // the element is already visble on the page). For a dropdown
31023
- // this means that the area will be scrolled to the top when
31024
- // the user interacts with the dropdown (since we actively
31025
- // manage dropdown header and option focus for keyboard users).
31026
- //
31027
- // To work around this bug the `preventScroll` focus option is
31028
- // used.
31029
- //
31030
- // Using `focusOptions` is an experimental browser feature
31031
- // "that should not be used in production code". However, since
31032
- // Chrome is the only browser we support that exhibits this
31033
- // buggy behavior and using `focusOptions` works around the
31034
- // bug, it is perfectly reasonable to use the feature in this
31035
- // case. We are not relying on the effect of the
31036
- // `preventScroll` option otherwise.
31037
- //
31038
- // focusOptions documentation:
31039
- //
31040
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
31041
- //
31042
- // Note that there could be cases where we want default focus() behavior.
31043
- // One that comes to mind is a dropdown with a long list of options that
31044
- // might be rendered off screen when opened. With this workaround the
31045
- // selected option would not be scrolled into view if the option is near
31046
- // the end of the list. We're not aware of any uses of the dropdown where
31047
- // this is an issue, however, so there isn't currently a way to out of the
31048
- // `preventScroll` behavior.
31049
-
31050
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element'.
31051
- element.focus({
31052
- preventScroll: true
31053
- });
31054
- }
31055
-
31056
31514
  const {
31057
31515
  Icon
31058
31516
  } = components;