@khanacademy/perseus-editor 14.0.1 → 14.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { addLibraryVersionToPerseusDebug, PerseusError, Errors } from '@khanacademy/perseus-core';
2
- import { constants, components, Widgets, icons, WIDGET_PROP_DENYLIST, Util, Log, PerseusMarkdown, preprocessTex, ApiOptions, buildMapper, itemToTree, buildEmptyItemTreeForShape, shapes, ItemExtras, itemDataVersion, ClassNames, MultiItems, traverse, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, PerseusExpressionAnswerFormConsidered, Expression, interactiveSizes, KhanMath, GrapherWidget, GrapherUtil, SizingUtils, PerseusI18nContext, KhanColors, ColorPicker, DashPicker, ArrowPicker, ElementContainer, getAngleCoords, getPolygonCoords, getPointCoords, getQuadraticCoords, getSinusoidCoords, getCircleCoords, getLinearSystemCoords, getSegmentCoords, getLineCoords, lockedFigureColors, lockedFigureFillStyles, InteractiveGraphWidget, globalStyles, globalConstants, MatrixWidget, makeSafeUrl, PlotterWidget, plotterPlotTypes, BaseRadio, TableWidget, widgets } from '@khanacademy/perseus';
2
+ import { constants, components, Widgets, icons, WIDGET_PROP_DENYLIST, Util, Log, PerseusMarkdown, preprocessTex, ApiOptions, buildMapper, itemToTree, buildEmptyItemTreeForShape, shapes, ItemExtras, itemDataVersion, ClassNames, MultiItems, traverse, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, PerseusExpressionAnswerFormConsidered, Expression, interactiveSizes, KhanMath, GrapherWidget, GrapherUtil, SizingUtils, PerseusI18nContext, KhanColors, getAngleCoords, getPolygonCoords, getPointCoords, getQuadraticCoords, getSinusoidCoords, getCircleCoords, getLinearSystemCoords, getSegmentCoords, getLineCoords, lockedFigureColors, lockedFigureFillStyles, InteractiveGraphWidget, globalStyles, globalConstants, MatrixWidget, makeSafeUrl, PlotterWidget, plotterPlotTypes, BaseRadio, TableWidget, widgets } from '@khanacademy/perseus';
3
3
  export { widgets } from '@khanacademy/perseus';
4
4
  import * as React from 'react';
5
- import { useRef, useEffect, useState } from 'react';
5
+ import { useRef, useEffect, useState, useId } from 'react';
6
6
  import _ from 'underscore';
7
7
  import createReactClass from 'create-react-class';
8
8
  import Clickable from '@khanacademy/wonder-blocks-clickable';
@@ -15,7 +15,7 @@ import Switch from '@khanacademy/wonder-blocks-switch';
15
15
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
16
16
  import caretRight from '@phosphor-icons/core/bold/caret-right-bold.svg';
17
17
  import classNames from 'classnames';
18
- import { Checkbox as Checkbox$1, LabeledTextField, TextField } from '@khanacademy/wonder-blocks-form';
18
+ import { Checkbox as Checkbox$1, LabeledTextField, TextField, TextArea } from '@khanacademy/wonder-blocks-form';
19
19
  import ReactDOM from 'react-dom';
20
20
  import * as KAS from '@khanacademy/kas';
21
21
  import Button from '@khanacademy/wonder-blocks-button';
@@ -31,6 +31,8 @@ import caretDoubleUpIcon from '@phosphor-icons/core/bold/caret-double-up-bold.sv
31
31
  import caretDownIcon from '@phosphor-icons/core/bold/caret-down-bold.svg';
32
32
  import caretUpIcon from '@phosphor-icons/core/bold/caret-up-bold.svg';
33
33
  import trashIcon from '@phosphor-icons/core/bold/trash-bold.svg';
34
+ import copyIcon from '@phosphor-icons/core/assets/regular/copy.svg';
35
+ import autoPasteIcon from '@phosphor-icons/core/assets/regular/note-pencil.svg';
34
36
  import arrowFatDown from '@phosphor-icons/core/regular/arrow-fat-down.svg';
35
37
  import arrowFatLeft from '@phosphor-icons/core/regular/arrow-fat-left.svg';
36
38
  import arrowFatRight from '@phosphor-icons/core/regular/arrow-fat-right.svg';
@@ -42,7 +44,7 @@ import arrowCounterClockwise from '@phosphor-icons/core/bold/arrow-counter-clock
42
44
 
43
45
  // This file is processed by a Rollup plugin (replace) to inject the production
44
46
  const libName = "@khanacademy/perseus-editor";
45
- const libVersion = "14.0.1";
47
+ const libVersion = "14.1.1";
46
48
  addLibraryVersionToPerseusDebug(libName, libVersion);
47
49
 
48
50
  function _extends() {
@@ -209,7 +211,7 @@ const JsonEditor = createReactClass({
209
211
  });
210
212
 
211
213
  const {
212
- InlineIcon: InlineIcon$a
214
+ InlineIcon: InlineIcon$b
213
215
  } = components;
214
216
  class SectionControlButton extends React.Component {
215
217
  render() {
@@ -227,15 +229,15 @@ class SectionControlButton extends React.Component {
227
229
  role: "button",
228
230
  hideDefaultFocusRing: true,
229
231
  "aria-label": title,
230
- style: styles$T.button
232
+ style: styles$U.button
231
233
  }, ({
232
234
  hovered,
233
235
  focused,
234
236
  pressed
235
- }) => /*#__PURE__*/React.createElement(InlineIcon$a, icon));
237
+ }) => /*#__PURE__*/React.createElement(InlineIcon$b, icon));
236
238
  }
237
239
  }
238
- const styles$T = StyleSheet.create({
240
+ const styles$U = StyleSheet.create({
239
241
  button: {
240
242
  backgroundColor: color.gold,
241
243
  margin: 2,
@@ -20626,11 +20628,11 @@ class Spring extends React.Component {
20626
20628
  } = this.props;
20627
20629
  return React.createElement(View, {
20628
20630
  "aria-hidden": "true",
20629
- style: [styles$S.grow, style]
20631
+ style: [styles$T.grow, style]
20630
20632
  });
20631
20633
  }
20632
20634
  }
20633
- const styles$S = StyleSheet.create({
20635
+ const styles$T = StyleSheet.create({
20634
20636
  grow: {
20635
20637
  flexGrow: 1
20636
20638
  }
@@ -20674,7 +20676,7 @@ const Spacing = {
20674
20676
 
20675
20677
  const _excluded$4 = ["label"];
20676
20678
  const {
20677
- InlineIcon: InlineIcon$9
20679
+ InlineIcon: InlineIcon$a
20678
20680
  } = components;
20679
20681
  const {
20680
20682
  iconChevronDown: iconChevronDown$1,
@@ -20780,7 +20782,7 @@ class WidgetEditor extends React.Component {
20780
20782
  className: "perseus-widget-editor-title-id",
20781
20783
  href: "#",
20782
20784
  onClick: this._toggleWidget
20783
- }, this.props.id, this.state.showWidget ? /*#__PURE__*/React.createElement(InlineIcon$9, iconChevronDown$1) : /*#__PURE__*/React.createElement(InlineIcon$9, iconChevronRight)), supportsStaticMode && /*#__PURE__*/React.createElement(LabeledSwitch$1, {
20785
+ }, this.props.id, this.state.showWidget ? /*#__PURE__*/React.createElement(InlineIcon$a, iconChevronDown$1) : /*#__PURE__*/React.createElement(InlineIcon$a, iconChevronRight)), supportsStaticMode && /*#__PURE__*/React.createElement(LabeledSwitch$1, {
20784
20786
  label: "Static",
20785
20787
  checked: !!widgetInfo.static,
20786
20788
  onChange: this._setStatic
@@ -20867,13 +20869,13 @@ class WidgetSelect extends React.Component {
20867
20869
  * that collapse a region.
20868
20870
  */
20869
20871
  function ToggleableCaret(props) {
20870
- const iconStyle = props.isExpanded ? styles$R.expanded : styles$R.collapsed;
20872
+ const iconStyle = props.isExpanded ? styles$S.expanded : styles$S.collapsed;
20871
20873
  return /*#__PURE__*/React.createElement(PhosphorIcon, {
20872
20874
  icon: caretRight,
20873
20875
  style: iconStyle
20874
20876
  });
20875
20877
  }
20876
- const styles$R = StyleSheet.create({
20878
+ const styles$S = StyleSheet.create({
20877
20879
  collapsed: {
20878
20880
  transition: ".15s"
20879
20881
  },
@@ -20906,16 +20908,16 @@ class TexErrorView extends React.Component {
20906
20908
  showErrors
20907
20909
  } = this.state;
20908
20910
  return /*#__PURE__*/React.createElement(View, {
20909
- style: styles$Q.errorContainer
20911
+ style: styles$R.errorContainer
20910
20912
  }, /*#__PURE__*/React.createElement(View, {
20911
- style: styles$Q.title,
20913
+ style: styles$R.title,
20912
20914
  onClick: this.handleToggleTexErrors
20913
20915
  }, /*#__PURE__*/React.createElement(ToggleableCaret, {
20914
20916
  isExpanded: showErrors
20915
20917
  }), "\xA0 TeX Errors (", errorList.length, ")"), showErrors && /*#__PURE__*/React.createElement(View, {
20916
- style: styles$Q.errorExplanation
20918
+ style: styles$R.errorExplanation
20917
20919
  }, "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, {
20918
- style: styles$Q.error,
20920
+ style: styles$R.error,
20919
20921
  key: index
20920
20922
  }, /*#__PURE__*/React.createElement(View, {
20921
20923
  style: {
@@ -20924,7 +20926,7 @@ class TexErrorView extends React.Component {
20924
20926
  }, e.math), /*#__PURE__*/React.createElement(View, null, e.message))));
20925
20927
  }
20926
20928
  }
20927
- const styles$Q = StyleSheet.create({
20929
+ const styles$R = StyleSheet.create({
20928
20930
  title: {
20929
20931
  backgroundColor: "#eee",
20930
20932
  fontSize: "1.25em",
@@ -21913,7 +21915,7 @@ class IframeContentRenderer extends React.Component {
21913
21915
 
21914
21916
  const {
21915
21917
  HUD: HUD$1,
21916
- InlineIcon: InlineIcon$8
21918
+ InlineIcon: InlineIcon$9
21917
21919
  } = components;
21918
21920
  const {
21919
21921
  iconCircleArrowDown: iconCircleArrowDown$1,
@@ -22072,7 +22074,7 @@ class ArticleEditor extends React.Component {
22072
22074
  onClick: () => {
22073
22075
  this._handleAddSectionAfter(this._sections().length - 1);
22074
22076
  }
22075
- }, /*#__PURE__*/React.createElement(InlineIcon$8, iconPlus$4), " Add a section")));
22077
+ }, /*#__PURE__*/React.createElement(InlineIcon$9, iconPlus$4), " Add a section")));
22076
22078
  }
22077
22079
  _renderLinterHUD() {
22078
22080
  return /*#__PURE__*/React.createElement(HUD$1, {
@@ -22219,8 +22221,8 @@ ArticleEditor.defaultProps = {
22219
22221
  * Renders three buttons: "Phone", "Tablet", and "Desktop".
22220
22222
  */
22221
22223
  const {
22222
- ButtonGroup: ButtonGroup$5,
22223
- InlineIcon: InlineIcon$7
22224
+ ButtonGroup: ButtonGroup$8,
22225
+ InlineIcon: InlineIcon$8
22224
22226
  } = components;
22225
22227
  const {
22226
22228
  devices
@@ -22231,14 +22233,14 @@ const {
22231
22233
  iconTablet
22232
22234
  } = icons;
22233
22235
  const ViewportResizer = props => {
22234
- const phoneButtonContents = /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(InlineIcon$7, iconMobilePhone), " Phone");
22235
- const tabletButtonContents = /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(InlineIcon$7, iconTablet), " Tablet");
22236
- const desktopButtonContents = /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(InlineIcon$7, iconDesktop), " Desktop");
22236
+ const phoneButtonContents = /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(InlineIcon$8, iconMobilePhone), " Phone");
22237
+ const tabletButtonContents = /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(InlineIcon$8, iconTablet), " Tablet");
22238
+ const desktopButtonContents = /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(InlineIcon$8, iconDesktop), " Desktop");
22237
22239
 
22238
22240
  // TODO(david): Allow input of custom viewport sizes.
22239
22241
  return /*#__PURE__*/React.createElement("span", {
22240
22242
  className: "viewport-resizer"
22241
- }, "Viewport:", " ", /*#__PURE__*/React.createElement(ButtonGroup$5, {
22243
+ }, "Viewport:", " ", /*#__PURE__*/React.createElement(ButtonGroup$8, {
22242
22244
  value: props.deviceType,
22243
22245
  allowEmpty: false,
22244
22246
  buttons: [{
@@ -23581,7 +23583,7 @@ class StructuredItemDiff extends React.Component {
23581
23583
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
23582
23584
  const {
23583
23585
  InfoTip: InfoTip$m,
23584
- InlineIcon: InlineIcon$6
23586
+ InlineIcon: InlineIcon$7
23585
23587
  } = components;
23586
23588
  const {
23587
23589
  iconCircleArrowDown,
@@ -23648,11 +23650,11 @@ class HintEditor extends React.Component {
23648
23650
  type: "button",
23649
23651
  className: this.props.isLast ? "hidden" : "",
23650
23652
  onClick: _.partial(this.props.onMove, 1)
23651
- }, /*#__PURE__*/React.createElement(InlineIcon$6, iconCircleArrowDown)), " ", /*#__PURE__*/React.createElement("button", {
23653
+ }, /*#__PURE__*/React.createElement(InlineIcon$7, iconCircleArrowDown)), " ", /*#__PURE__*/React.createElement("button", {
23652
23654
  type: "button",
23653
23655
  className: this.props.isFirst ? "hidden" : "",
23654
23656
  onClick: _.partial(this.props.onMove, -1)
23655
- }, /*#__PURE__*/React.createElement(InlineIcon$6, iconCircleArrowUp)), " ", this.props.isLast && /*#__PURE__*/React.createElement(InfoTip$m, null, /*#__PURE__*/React.createElement("p", null, "The last hint is automatically bolded."))), /*#__PURE__*/React.createElement("input", {
23657
+ }, /*#__PURE__*/React.createElement(InlineIcon$7, iconCircleArrowUp)), " ", this.props.isLast && /*#__PURE__*/React.createElement(InfoTip$m, null, /*#__PURE__*/React.createElement("p", null, "The last hint is automatically bolded."))), /*#__PURE__*/React.createElement("input", {
23656
23658
  type: "checkbox"
23657
23659
  // @ts-expect-error - TS2322 - Type 'boolean | null | undefined' is not assignable to type 'boolean | undefined'.
23658
23660
  ,
@@ -23662,7 +23664,7 @@ class HintEditor extends React.Component {
23662
23664
  type: "button",
23663
23665
  className: "remove-hint simple-button orange",
23664
23666
  onClick: this.props.onRemove
23665
- }, /*#__PURE__*/React.createElement(InlineIcon$6, iconTrash$5), "Remove this hint", " ")));
23667
+ }, /*#__PURE__*/React.createElement(InlineIcon$7, iconTrash$5), "Remove this hint", " ")));
23666
23668
  }
23667
23669
  }
23668
23670
  HintEditor.defaultProps = {
@@ -23886,7 +23888,7 @@ class CombinedHintsEditor extends React.Component {
23886
23888
  type: "button",
23887
23889
  className: "add-hint simple-button orange",
23888
23890
  onClick: this.addHint
23889
- }, /*#__PURE__*/React.createElement(InlineIcon$6, iconPlus$3), " Add a hint"))));
23891
+ }, /*#__PURE__*/React.createElement(InlineIcon$7, iconPlus$3), " Add a hint"))));
23890
23892
  /* eslint-enable max-len */
23891
23893
  }
23892
23894
  }
@@ -24037,7 +24039,7 @@ ItemExtrasEditor.defaultProps = {
24037
24039
  zTable: false
24038
24040
  };
24039
24041
  const ItemExtraCheckbox = props => /*#__PURE__*/React.createElement(View, {
24040
- style: [styles$P.checkbox, props.indent ? styles$P.indented : undefined]
24042
+ style: [styles$Q.checkbox, props.indent ? styles$Q.indented : undefined]
24041
24043
  }, /*#__PURE__*/React.createElement(Checkbox$1, {
24042
24044
  label: /*#__PURE__*/React.createElement(View, {
24043
24045
  style: {
@@ -24047,7 +24049,7 @@ const ItemExtraCheckbox = props => /*#__PURE__*/React.createElement(View, {
24047
24049
  checked: props.checked,
24048
24050
  onChange: newCheckedState => props.onChange(newCheckedState)
24049
24051
  }));
24050
- const styles$P = StyleSheet.create({
24052
+ const styles$Q = StyleSheet.create({
24051
24053
  indented: {
24052
24054
  marginInlineStart: spacing.large_24
24053
24055
  }
@@ -24705,7 +24707,7 @@ class SimpleButton extends React.Component {
24705
24707
  } = _this$props,
24706
24708
  otherProps = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
24707
24709
  return /*#__PURE__*/React.createElement("div", _extends({
24708
- className: css(styles$O.baseButton, color === "green" && styles$O.green, color === "orange" && styles$O.orange),
24710
+ className: css(styles$P.baseButton, color === "green" && styles$P.green, color === "orange" && styles$P.orange),
24709
24711
  onClick: onClick
24710
24712
  }, otherProps), children);
24711
24713
  }
@@ -24720,7 +24722,7 @@ SimpleButton.defaultProps = {
24720
24722
  color: "green",
24721
24723
  onClick: () => {}
24722
24724
  };
24723
- const styles$O = StyleSheet.create({
24725
+ const styles$P = StyleSheet.create({
24724
24726
  baseButton: {
24725
24727
  top: "0",
24726
24728
  fontSize: "11px",
@@ -24770,7 +24772,7 @@ const _excluded$2 = ["depth"],
24770
24772
  _excluded4 = ["shape", "data", "path", "actions"],
24771
24773
  _excluded5 = ["shape", "data", "path"];
24772
24774
  const {
24773
- InlineIcon: InlineIcon$5
24775
+ InlineIcon: InlineIcon$6
24774
24776
  } = components;
24775
24777
  const {
24776
24778
  iconChevronDown,
@@ -24940,23 +24942,23 @@ const LeafContainer = ({
24940
24942
  }) => {
24941
24943
  const hasPreviewHeading = shape.type === "content" || shape.type === "hint";
24942
24944
  const previewHeading = hasPreviewHeading && /*#__PURE__*/React.createElement("div", {
24943
- className: css(styles$N.containerHeader)
24945
+ className: css(styles$O.containerHeader)
24944
24946
  }, /*#__PURE__*/React.createElement(Header, {
24945
24947
  depth: path.length,
24946
- className: css(styles$N.containerTitle)
24948
+ className: css(styles$O.containerTitle)
24947
24949
  }, capitalize(name)));
24948
24950
  return /*#__PURE__*/React.createElement("div", {
24949
- className: css(styles$N.container)
24951
+ className: css(styles$O.container)
24950
24952
  }, /*#__PURE__*/React.createElement("span", {
24951
- className: css(styles$N.row, styles$N.rowHeading)
24953
+ className: css(styles$O.row, styles$O.rowHeading)
24952
24954
  }, /*#__PURE__*/React.createElement("div", {
24953
- className: css(styles$N.columnLeft)
24955
+ className: css(styles$O.columnLeft)
24954
24956
  }, /*#__PURE__*/React.createElement("div", {
24955
- className: "pod-title " + css(styles$N.containerHeader)
24957
+ className: "pod-title " + css(styles$O.containerHeader)
24956
24958
  }, /*#__PURE__*/React.createElement("div", {
24957
- className: css(styles$N.containerTitle)
24959
+ className: css(styles$O.containerTitle)
24958
24960
  }, capitalize(name)), controls)), /*#__PURE__*/React.createElement("div", {
24959
- className: css(styles$N.columnRight)
24961
+ className: css(styles$O.columnRight)
24960
24962
  }, previewHeading)), children);
24961
24963
  };
24962
24964
  const ArrayContainer = props => {
@@ -24969,11 +24971,11 @@ const ArrayContainer = props => {
24969
24971
  actions
24970
24972
  } = props;
24971
24973
  return /*#__PURE__*/React.createElement("div", {
24972
- className: css(styles$N.container)
24974
+ className: css(styles$O.container)
24973
24975
  }, controls && /*#__PURE__*/React.createElement("div", {
24974
- className: css(styles$N.columnLeft, styles$N.containerHeader)
24976
+ className: css(styles$O.columnLeft, styles$O.containerHeader)
24975
24977
  }, controls), /*#__PURE__*/React.createElement("div", null, children), /*#__PURE__*/React.createElement("div", {
24976
- className: css(styles$N.columnLeft)
24978
+ className: css(styles$O.columnLeft)
24977
24979
  }, /*#__PURE__*/React.createElement("a", {
24978
24980
  href: "javascript:void 0",
24979
24981
  onClick: () => actions.addArrayElement(path, shape.elementShape)
@@ -24986,28 +24988,28 @@ const ObjectContainer = ({
24986
24988
  path
24987
24989
  }) => {
24988
24990
  const headingEditor = /*#__PURE__*/React.createElement("div", {
24989
- className: css(styles$N.containerHeader)
24991
+ className: css(styles$O.containerHeader)
24990
24992
  }, /*#__PURE__*/React.createElement(Header, {
24991
24993
  depth: path.length,
24992
- className: css(styles$N.containerTitle)
24994
+ className: css(styles$O.containerTitle)
24993
24995
  }, capitalize(name)), controls);
24994
24996
  const headingPreview = (name || controls) && /*#__PURE__*/React.createElement("div", {
24995
- className: css(styles$N.containerHeader, styles$N.previewCollectionHeader)
24997
+ className: css(styles$O.containerHeader, styles$O.previewCollectionHeader)
24996
24998
  }, /*#__PURE__*/React.createElement(Header, {
24997
24999
  depth: path.length,
24998
- className: css(styles$N.containerTitle)
25000
+ className: css(styles$O.containerTitle)
24999
25001
  }, capitalize(name)));
25000
25002
  const hasBothHeadings = headingEditor && headingPreview;
25001
25003
  return /*#__PURE__*/React.createElement("div", {
25002
- className: css(styles$N.container)
25004
+ className: css(styles$O.container)
25003
25005
  }, hasBothHeadings && /*#__PURE__*/React.createElement("span", {
25004
- className: css(styles$N.row)
25006
+ className: css(styles$O.row)
25005
25007
  }, /*#__PURE__*/React.createElement("div", {
25006
- className: css(styles$N.columnLeft)
25008
+ className: css(styles$O.columnLeft)
25007
25009
  }, headingEditor), /*#__PURE__*/React.createElement("div", {
25008
- className: css(styles$N.columnRight)
25010
+ className: css(styles$O.columnRight)
25009
25011
  }, headingPreview)), /*#__PURE__*/React.createElement("div", {
25010
- className: css(path.length > 0 && styles$N.contentIndent)
25012
+ className: css(path.length > 0 && styles$O.contentIndent)
25011
25013
  }, children));
25012
25014
  };
25013
25015
 
@@ -25130,11 +25132,11 @@ const ItemNodeContent = withStickiness(props => {
25130
25132
  className: "framework-perseus"
25131
25133
  }, lens(renderers).get(path));
25132
25134
  return /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("div", {
25133
- className: css(styles$N.row)
25135
+ className: css(styles$O.row)
25134
25136
  }, /*#__PURE__*/React.createElement("div", {
25135
- className: css(styles$N.columnLeft)
25137
+ className: css(styles$O.columnLeft)
25136
25138
  }, /*#__PURE__*/React.createElement("div", {
25137
- className: css(sticky && styles$N.sticky)
25139
+ className: css(sticky && styles$O.sticky)
25138
25140
  },
25139
25141
  /*#__PURE__*/
25140
25142
  // @ts-expect-error - TS2769 - No overload matches this call.
@@ -25142,9 +25144,9 @@ const ItemNodeContent = withStickiness(props => {
25142
25144
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
25143
25145
  apiOptions: apiOptions
25144
25146
  })))), /*#__PURE__*/React.createElement("div", {
25145
- className: css(styles$N.columnRight)
25147
+ className: css(styles$O.columnRight)
25146
25148
  }, /*#__PURE__*/React.createElement("div", {
25147
- className: css(sticky && styles$N.sticky)
25149
+ className: css(sticky && styles$O.sticky)
25148
25150
  }, preview))));
25149
25151
  });
25150
25152
  const HintNodeContent = withStickiness(props => {
@@ -25160,13 +25162,13 @@ const HintNodeContent = withStickiness(props => {
25160
25162
  className: "framework-perseus"
25161
25163
  }, lens(renderers).get(path));
25162
25164
  return /*#__PURE__*/React.createElement("div", {
25163
- className: css(styles$N.row)
25165
+ className: css(styles$O.row)
25164
25166
  }, /*#__PURE__*/React.createElement("div", {
25165
- className: css(styles$N.columnLeft)
25167
+ className: css(styles$O.columnLeft)
25166
25168
  }, /*#__PURE__*/React.createElement("div", {
25167
- className: css(sticky && styles$N.sticky)
25169
+ className: css(sticky && styles$O.sticky)
25168
25170
  }, /*#__PURE__*/React.createElement(HintEditor, _extends({}, data, {
25169
- className: css(styles$N.hintEditor),
25171
+ className: css(styles$O.hintEditor),
25170
25172
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
25171
25173
  apiOptions: apiOptions,
25172
25174
  showTitle: false,
@@ -25181,9 +25183,9 @@ const HintNodeContent = withStickiness(props => {
25181
25183
  isFirst: true,
25182
25184
  isLast: true
25183
25185
  })))), /*#__PURE__*/React.createElement("div", {
25184
- className: css(styles$N.columnRight)
25186
+ className: css(styles$O.columnRight)
25185
25187
  }, /*#__PURE__*/React.createElement("div", {
25186
- className: css(sticky && styles$N.sticky)
25188
+ className: css(sticky && styles$O.sticky)
25187
25189
  }, preview)));
25188
25190
  });
25189
25191
  const TagsNodeContent = props => {
@@ -25200,9 +25202,9 @@ const TagsNodeContent = props => {
25200
25202
  return null;
25201
25203
  }
25202
25204
  return /*#__PURE__*/React.createElement("div", {
25203
- className: css(styles$N.columnLeft)
25205
+ className: css(styles$O.columnLeft)
25204
25206
  }, /*#__PURE__*/React.createElement("div", {
25205
- className: css(styles$N.tagsEditor)
25207
+ className: css(styles$O.tagsEditor)
25206
25208
  }, /*#__PURE__*/React.createElement(GroupMetadataEditor, {
25207
25209
  value: data,
25208
25210
  onChange: newVal => actions.setValueAtPath(path, newVal),
@@ -25233,31 +25235,31 @@ const ArrayNodeContent = props => {
25233
25235
  const subpath = path.concat(i);
25234
25236
  const controls = [i > 0 && /*#__PURE__*/React.createElement("div", {
25235
25237
  key: "moveArrayElementUp",
25236
- className: css(styles$N.control)
25238
+ className: css(styles$O.control)
25237
25239
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25238
25240
  color: "orange",
25239
25241
  title: "Move up",
25240
25242
  onClick: () => actions.moveArrayElementUp(subpath)
25241
25243
  }, /*#__PURE__*/React.createElement("div", {
25242
- className: css(styles$N.verticalFlip)
25243
- }, /*#__PURE__*/React.createElement(InlineIcon$5, iconChevronDown)))), i < data.length - 1 && /*#__PURE__*/React.createElement("div", {
25244
+ className: css(styles$O.verticalFlip)
25245
+ }, /*#__PURE__*/React.createElement(InlineIcon$6, iconChevronDown)))), i < data.length - 1 && /*#__PURE__*/React.createElement("div", {
25244
25246
  key: "moveArrayElementDown",
25245
- className: css(styles$N.control)
25247
+ className: css(styles$O.control)
25246
25248
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25247
25249
  color: "orange",
25248
25250
  title: "Move down",
25249
25251
  onClick: () => actions.moveArrayElementDown(subpath)
25250
- }, /*#__PURE__*/React.createElement(InlineIcon$5, iconChevronDown))), /*#__PURE__*/React.createElement("div", {
25252
+ }, /*#__PURE__*/React.createElement(InlineIcon$6, iconChevronDown))), /*#__PURE__*/React.createElement("div", {
25251
25253
  key: "removeArrayElement",
25252
- className: css(styles$N.control)
25254
+ className: css(styles$O.control)
25253
25255
  }, /*#__PURE__*/React.createElement(SimpleButton, {
25254
25256
  color: "orange",
25255
25257
  title: "Delete",
25256
25258
  onClick: () => actions.removeArrayElement(subpath)
25257
- }, /*#__PURE__*/React.createElement(InlineIcon$5, iconTrash$4)))];
25259
+ }, /*#__PURE__*/React.createElement(InlineIcon$6, iconTrash$4)))];
25258
25260
  return /*#__PURE__*/React.createElement("div", {
25259
25261
  key: i,
25260
- className: css(styles$N.arrayElement, !elementIsLeaf && styles$N.arrayElementAndNotLeaf)
25262
+ className: css(styles$O.arrayElement, !elementIsLeaf && styles$O.arrayElementAndNotLeaf)
25261
25263
  }, /*#__PURE__*/React.createElement(NodeContainer, _extends({}, otherProps, {
25262
25264
  key: i,
25263
25265
  shape: shape.elementShape,
@@ -25283,7 +25285,7 @@ const ObjectNodeContent = props => {
25283
25285
  // made sense to the shape's author is the order in which we'll iterate :)
25284
25286
  const children = Object.keys(shape.shape).map(subkey => /*#__PURE__*/React.createElement("div", {
25285
25287
  key: subkey,
25286
- className: css(styles$N.objectElement)
25288
+ className: css(styles$O.objectElement)
25287
25289
  }, /*#__PURE__*/React.createElement(NodeContainer, _extends({}, otherProps, {
25288
25290
  shape: shape.shape[subkey],
25289
25291
  data: data[subkey],
@@ -25450,7 +25452,7 @@ class MultiRendererEditor extends React.Component {
25450
25452
  }, this._renderContent());
25451
25453
  }
25452
25454
  }
25453
- const styles$N = StyleSheet.create({
25455
+ const styles$O = StyleSheet.create({
25454
25456
  // This is used in a number of places throughout this file.
25455
25457
  container: {},
25456
25458
  // eslint-disable-next-line react-native/no-unused-styles
@@ -26008,7 +26010,7 @@ DeprecatedStandinEditor.widgetName = "deprecated-standin";
26008
26010
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
26009
26011
  const {
26010
26012
  InfoTip: InfoTip$j,
26011
- InlineIcon: InlineIcon$4
26013
+ InlineIcon: InlineIcon$5
26012
26014
  } = components;
26013
26015
  const {
26014
26016
  iconPlus: iconPlus$2,
@@ -26128,14 +26130,14 @@ class DropdownEditor extends React.Component {
26128
26130
  this, i)
26129
26131
  }, /*#__PURE__*/React.createElement("span", {
26130
26132
  className: "remove-choice"
26131
- }, /*#__PURE__*/React.createElement(InlineIcon$4, iconTrash$3)))));
26133
+ }, /*#__PURE__*/React.createElement(InlineIcon$5, iconTrash$3)))));
26132
26134
  }, this)), /*#__PURE__*/React.createElement("div", {
26133
26135
  className: "add-choice-container"
26134
26136
  }, /*#__PURE__*/React.createElement("a", {
26135
26137
  href: "#",
26136
26138
  className: "simple-button orange",
26137
26139
  onClick: this.addChoice
26138
- }, /*#__PURE__*/React.createElement(InlineIcon$4, iconPlus$2), " Add a choice", " ")));
26140
+ }, /*#__PURE__*/React.createElement(InlineIcon$5, iconPlus$2), " Add a choice", " ")));
26139
26141
  }
26140
26142
  }
26141
26143
  DropdownEditor.propTypes = {
@@ -26396,7 +26398,7 @@ const SortableItem = createReactClass({
26396
26398
  }
26397
26399
  return /*#__PURE__*/React.createElement("li", {
26398
26400
  draggable: this.props.draggable,
26399
- className: [dragState, css(styles$M.sortableListItem)].join(" "),
26401
+ className: [dragState, css(styles$N.sortableListItem)].join(" "),
26400
26402
  onDragStart: this.handleDragStart,
26401
26403
  onDrop: this.handleDrop,
26402
26404
  onDragEnter: this.handleDragEnter,
@@ -26404,7 +26406,7 @@ const SortableItem = createReactClass({
26404
26406
  }, this.props.component);
26405
26407
  }
26406
26408
  });
26407
- const styles$M = StyleSheet.create({
26409
+ const styles$N = StyleSheet.create({
26408
26410
  sortableListItem: {
26409
26411
  margin: "5px 0"
26410
26412
  }
@@ -26652,13 +26654,13 @@ class ExpressionEditor extends React.Component {
26652
26654
  onChange: this.handleToggleDiv
26653
26655
  }));
26654
26656
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(HeadingSmall, null, "Global Options"), /*#__PURE__*/React.createElement("div", {
26655
- className: css(styles$L.paddedY)
26657
+ className: css(styles$M.paddedY)
26656
26658
  }, /*#__PURE__*/React.createElement(LabeledTextField, {
26657
26659
  label: "Visible label",
26658
26660
  value: this.props.visibleLabel || "",
26659
26661
  onChange: this.change("visibleLabel")
26660
26662
  }), /*#__PURE__*/React.createElement(InfoTip$i, null, /*#__PURE__*/React.createElement("p", null, "Optional visible text; strongly encouraged to help learners using dictation software, but can be omitted if the surrounding content provides enough context."))), /*#__PURE__*/React.createElement("div", {
26661
- className: css(styles$L.paddedY)
26663
+ className: css(styles$M.paddedY)
26662
26664
  }, /*#__PURE__*/React.createElement(LabeledTextField, {
26663
26665
  label: "Aria label",
26664
26666
  value: this.props.ariaLabel || "",
@@ -26667,13 +26669,13 @@ class ExpressionEditor extends React.Component {
26667
26669
  href: "https://www.w3.org/WAI/tips/designing/#ensure-that-form-elements-include-clearly-associated-labels",
26668
26670
  target: "_blank"
26669
26671
  }, "this article.")))), /*#__PURE__*/React.createElement("div", {
26670
- className: css(styles$L.paddedY)
26672
+ className: css(styles$M.paddedY)
26671
26673
  }, /*#__PURE__*/React.createElement(LabeledTextField, {
26672
26674
  label: "Function variables",
26673
26675
  value: this.state.functionsInternal,
26674
26676
  onChange: this.handleFunctions
26675
26677
  }), /*#__PURE__*/React.createElement(InfoTip$i, null, /*#__PURE__*/React.createElement("p", null, "Single-letter variables listed here will be interpreted as functions. This let us know that f(x) means \"f of x\" and not \"f times x\"."))), /*#__PURE__*/React.createElement("div", {
26676
- className: css(styles$L.paddedY)
26678
+ className: css(styles$M.paddedY)
26677
26679
  }, /*#__PURE__*/React.createElement(Checkbox$1, {
26678
26680
  label: "Use \xD7 instead of \u22C5 for multiplication",
26679
26681
  checked: this.props.times,
@@ -26683,7 +26685,7 @@ class ExpressionEditor extends React.Component {
26683
26685
  });
26684
26686
  }
26685
26687
  }), /*#__PURE__*/React.createElement(InfoTip$i, null, /*#__PURE__*/React.createElement("p", null, "For pre-algebra problems this option displays multiplication as \\times instead of \\cdot in both the rendered output and the acceptable formats examples."))), /*#__PURE__*/React.createElement("div", {
26686
- className: css(styles$L.paddedY)
26688
+ className: css(styles$M.paddedY)
26687
26689
  }, /*#__PURE__*/React.createElement(HeadingXSmall, null, "Button Sets"), buttonSetChoices), /*#__PURE__*/React.createElement(HeadingSmall, null, "Answers"), /*#__PURE__*/React.createElement("p", {
26688
26690
  style: {
26689
26691
  margin: "4px 0"
@@ -26754,18 +26756,18 @@ class AnswerOption extends React.Component {
26754
26756
  color: "destructive",
26755
26757
  light: true
26756
26758
  }, "Delete");
26757
- const answerStatusCss = css(styles$L.answerStatus, this.props.considered === "wrong" && styles$L.answerStatusWrong, this.props.considered === "correct" && styles$L.answerStatusCorrect, this.props.considered === "ungraded" && styles$L.answerStatusUngraded);
26759
+ const answerStatusCss = css(styles$M.answerStatus, this.props.considered === "wrong" && styles$M.answerStatusWrong, this.props.considered === "correct" && styles$M.answerStatusCorrect, this.props.considered === "ungraded" && styles$M.answerStatusUngraded);
26758
26760
  return /*#__PURE__*/React.createElement("div", {
26759
- className: css(styles$L.answerOption)
26761
+ className: css(styles$M.answerOption)
26760
26762
  }, /*#__PURE__*/React.createElement("div", {
26761
- className: css(styles$L.answerHandle)
26763
+ className: css(styles$M.answerHandle)
26762
26764
  }), /*#__PURE__*/React.createElement("div", {
26763
- className: css(styles$L.answerBody)
26765
+ className: css(styles$M.answerBody)
26764
26766
  }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
26765
26767
  onClick: this.toggleConsidered,
26766
26768
  className: answerStatusCss
26767
26769
  }, this.props.considered), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Expression, this.props.expressionProps))), /*#__PURE__*/React.createElement("div", {
26768
- className: css(styles$L.paddedY, styles$L.paddedX)
26770
+ className: css(styles$M.paddedY, styles$M.paddedX)
26769
26771
  }, /*#__PURE__*/React.createElement(Checkbox$1, {
26770
26772
  label: "Answer expression must have the same form.",
26771
26773
  checked: this.props.form,
@@ -26775,7 +26777,7 @@ class AnswerOption extends React.Component {
26775
26777
  });
26776
26778
  }
26777
26779
  }), /*#__PURE__*/React.createElement(InfoTip$i, null, /*#__PURE__*/React.createElement("p", null, "The student's answer must be in the same form. Commutativity and excess negative signs are ignored."))), /*#__PURE__*/React.createElement("div", {
26778
- className: css(styles$L.paddedY, styles$L.paddedX)
26780
+ className: css(styles$M.paddedY, styles$M.paddedX)
26779
26781
  }, /*#__PURE__*/React.createElement(Checkbox$1, {
26780
26782
  label: "Answer expression must be fully expanded and simplified.",
26781
26783
  checked: this.props.simplify,
@@ -26785,11 +26787,11 @@ class AnswerOption extends React.Component {
26785
26787
  });
26786
26788
  }
26787
26789
  }), /*#__PURE__*/React.createElement(InfoTip$i, null, /*#__PURE__*/React.createElement("p", null, "The student's answer must be fully expanded and simplified. Answering this equation (x^2+2x+1) with this factored equation (x+1)^2 will render this response \"Your answer is not fully expanded and simplified.\""))), /*#__PURE__*/React.createElement("div", {
26788
- className: css(styles$L.buttonRow, styles$L.paddedY)
26790
+ className: css(styles$M.buttonRow, styles$M.paddedY)
26789
26791
  }, removeButton)));
26790
26792
  }
26791
26793
  }
26792
- const styles$L = StyleSheet.create({
26794
+ const styles$M = StyleSheet.create({
26793
26795
  paddedX: {
26794
26796
  paddingLeft: spacing.xSmall_8,
26795
26797
  paddingRight: spacing.xSmall_8
@@ -26834,7 +26836,7 @@ const styles$L = StyleSheet.create({
26834
26836
  });
26835
26837
 
26836
26838
  const {
26837
- InlineIcon: InlineIcon$3,
26839
+ InlineIcon: InlineIcon$4,
26838
26840
  TextInput: TextInput$5
26839
26841
  } = components;
26840
26842
  const {
@@ -26884,10 +26886,10 @@ class GradedGroupEditor extends React.Component {
26884
26886
  }, /*#__PURE__*/React.createElement("div", {
26885
26887
  className: "perseus-widget-row"
26886
26888
  }, /*#__PURE__*/React.createElement("label", {
26887
- className: css(styles$K.title)
26889
+ className: css(styles$L.title)
26888
26890
  }, "Title:", " ", /*#__PURE__*/React.createElement(TextInput$5, {
26889
26891
  value: this.props.title,
26890
- className: css(styles$K.input)
26892
+ className: css(styles$L.input)
26891
26893
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
26892
26894
  ,
26893
26895
  onChange: this.change("title")
@@ -26909,10 +26911,10 @@ class GradedGroupEditor extends React.Component {
26909
26911
  },
26910
26912
  className: "add-hint simple-button orange",
26911
26913
  onClick: this.handleAddHint
26912
- }, /*#__PURE__*/React.createElement(InlineIcon$3, iconPlus$1), " Add a hint"), this.props.hint && /*#__PURE__*/React.createElement("div", {
26914
+ }, /*#__PURE__*/React.createElement(InlineIcon$4, iconPlus$1), " Add a hint"), this.props.hint && /*#__PURE__*/React.createElement("div", {
26913
26915
  className: "perseus-hint-editor"
26914
26916
  }, /*#__PURE__*/React.createElement("div", {
26915
- className: css(styles$K.hintsTitle)
26917
+ className: css(styles$L.hintsTitle)
26916
26918
  }, "Hint"), /*#__PURE__*/React.createElement(Editor, {
26917
26919
  ref: this.hintEditor,
26918
26920
  content: this.props.hint ? this.props.hint.content : "",
@@ -26931,7 +26933,7 @@ class GradedGroupEditor extends React.Component {
26931
26933
  type: "button",
26932
26934
  className: "remove-hint simple-button orange",
26933
26935
  onClick: this.handleRemoveHint
26934
- }, /*#__PURE__*/React.createElement(InlineIcon$3, iconTrash$2), " Remove this hint")));
26936
+ }, /*#__PURE__*/React.createElement(InlineIcon$4, iconTrash$2), " Remove this hint")));
26935
26937
  }
26936
26938
  }
26937
26939
  GradedGroupEditor.propTypes = _extends({}, Changeable.propTypes, {
@@ -26949,7 +26951,7 @@ GradedGroupEditor.defaultProps = {
26949
26951
  images: {},
26950
26952
  hint: null
26951
26953
  };
26952
- const styles$K = StyleSheet.create({
26954
+ const styles$L = StyleSheet.create({
26953
26955
  title: {
26954
26956
  fontSize: 18,
26955
26957
  fontWeight: "bold"
@@ -27026,7 +27028,7 @@ GradedGroupSetEditor.defaultProps = {
27026
27028
  const setArrayItem = (list, i, value) => [...list.slice(0, i), value, ...list.slice(i + 1)];
27027
27029
 
27028
27030
  const {
27029
- ButtonGroup: ButtonGroup$4,
27031
+ ButtonGroup: ButtonGroup$7,
27030
27032
  InfoTip: InfoTip$h,
27031
27033
  RangeInput: RangeInput$6
27032
27034
  } = components;
@@ -27441,7 +27443,7 @@ const GraphSettings = createReactClass({
27441
27443
  onChange: this.changeSnapStep
27442
27444
  }))), /*#__PURE__*/React.createElement("div", {
27443
27445
  className: "perseus-widget-row"
27444
- }, /*#__PURE__*/React.createElement("label", null, "Markings: "), /*#__PURE__*/React.createElement(ButtonGroup$4, {
27446
+ }, /*#__PURE__*/React.createElement("label", null, "Markings: "), /*#__PURE__*/React.createElement(ButtonGroup$7, {
27445
27447
  value: this.props.markings,
27446
27448
  allowEmpty: false,
27447
27449
  buttons: [{
@@ -27866,7 +27868,7 @@ IframeEditor.defaultProps = {
27866
27868
  /* eslint-disable jsx-a11y/anchor-is-valid */
27867
27869
  const {
27868
27870
  InfoTip: InfoTip$f,
27869
- InlineIcon: InlineIcon$2,
27871
+ InlineIcon: InlineIcon$3,
27870
27872
  RangeInput: RangeInput$5
27871
27873
  } = components;
27872
27874
  const defaultBoxSize = 400;
@@ -27942,7 +27944,7 @@ class ImageEditor extends React.Component {
27942
27944
  // eslint-disable-next-line react/jsx-no-bind
27943
27945
  ,
27944
27946
  onClick: this.removeLabel.bind(this, i)
27945
- }, /*#__PURE__*/React.createElement(InlineIcon$2, icons.iconTrash))));
27947
+ }, /*#__PURE__*/React.createElement(InlineIcon$3, icons.iconTrash))));
27946
27948
  }
27947
27949
  change(...args) {
27948
27950
  return Changeable.change.apply(this, args);
@@ -28258,6 +28260,111 @@ InputNumberEditor.defaultProps = {
28258
28260
  rightAlign: false
28259
28261
  };
28260
28262
 
28263
+ /* eslint-disable jsx-a11y/anchor-is-valid */
28264
+ const {
28265
+ InlineIcon: InlineIcon$2
28266
+ } = components;
28267
+ class ElementContainer extends React.Component {
28268
+ constructor(props) {
28269
+ super(props);
28270
+ this.toggle = e => {
28271
+ e.preventDefault();
28272
+ this.setState({
28273
+ show: !this.state.show
28274
+ });
28275
+ };
28276
+ this.state = {
28277
+ show: props.initiallyVisible
28278
+ };
28279
+ }
28280
+ render() {
28281
+ return /*#__PURE__*/React.createElement("div", {
28282
+ className: "perseus-interaction-element"
28283
+ }, /*#__PURE__*/React.createElement("a", {
28284
+ href: "#",
28285
+ className: "perseus-interaction-element-title " + (this.state.show ? "open" : "closed"),
28286
+ onClick: this.toggle
28287
+ }, this.state.show ? /*#__PURE__*/React.createElement(InlineIcon$2, icons.iconChevronDown) : /*#__PURE__*/React.createElement(InlineIcon$2, icons.iconChevronRight), this.props.title), /*#__PURE__*/React.createElement("div", {
28288
+ className: "perseus-interaction-element-content " + (this.state.show ? "enter" : "leave")
28289
+ }, this.props.children, (this.props.onUp != null || this.props.onDown != null || this.props.onDelete != null) && /*#__PURE__*/React.createElement("div", {
28290
+ className: "edit-controls"
28291
+ }, this.props.onUp != null && /*#__PURE__*/React.createElement("button", {
28292
+ onClick: this.props.onUp
28293
+ }, /*#__PURE__*/React.createElement(InlineIcon$2, icons.iconCircleArrowUp)), this.props.onDown != null && /*#__PURE__*/React.createElement("button", {
28294
+ onClick: this.props.onDown
28295
+ }, /*#__PURE__*/React.createElement(InlineIcon$2, icons.iconCircleArrowDown)), this.props.onDelete != null && /*#__PURE__*/React.createElement("button", {
28296
+ onClick: this.props.onDelete
28297
+ }, /*#__PURE__*/React.createElement(InlineIcon$2, icons.iconTrash)))));
28298
+ }
28299
+ }
28300
+ ElementContainer.defaultProps = {
28301
+ initiallyVisible: false,
28302
+ title: "More"
28303
+ };
28304
+
28305
+ const {
28306
+ ButtonGroup: ButtonGroup$6
28307
+ } = components;
28308
+ const COLORS = [KhanColors.BLACK, KhanColors.BLUE, KhanColors.GREEN, KhanColors.PINK, KhanColors.PURPLE, KhanColors.RED, KhanColors.GRAY];
28309
+ const LIGHT_COLORS = [KhanColors.LIGHT_BLUE, KhanColors.LIGHT_ORANGE, KhanColors.LIGHT_PINK, KhanColors.LIGHT_GREEN, KhanColors.LIGHT_PURPLE, KhanColors.LIGHT_RED, "#fff"];
28310
+ class ColorPicker extends React.Component {
28311
+ render() {
28312
+ const colors = this.props.lightColors ? LIGHT_COLORS : COLORS;
28313
+ return /*#__PURE__*/React.createElement(ButtonGroup$6, {
28314
+ value: this.props.value,
28315
+ allowEmpty: false,
28316
+ buttons: _.map(colors, color => {
28317
+ return {
28318
+ value: color,
28319
+ content: /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("span", {
28320
+ className: "colorpicker-circle",
28321
+ style: {
28322
+ background: color
28323
+ }
28324
+ }), "\xA0")
28325
+ };
28326
+ }),
28327
+ onChange: this.props.onChange
28328
+ });
28329
+ }
28330
+ }
28331
+ ColorPicker.defaultProps = {
28332
+ value: KhanColors.BLACK,
28333
+ lightColors: false
28334
+ };
28335
+
28336
+ const {
28337
+ ButtonGroup: ButtonGroup$5
28338
+ } = components;
28339
+ class DashPicker extends React.Component {
28340
+ render() {
28341
+ return /*#__PURE__*/React.createElement(ButtonGroup$5, {
28342
+ value: this.props.value,
28343
+ allowEmpty: false,
28344
+ buttons: [{
28345
+ value: "",
28346
+ content: /*#__PURE__*/React.createElement("span", null, "\u2014")
28347
+ }, {
28348
+ value: "-",
28349
+ content: /*#__PURE__*/React.createElement("span", null, "\u2013\u2013\u2013")
28350
+ }, {
28351
+ value: "- ",
28352
+ content: /*#__PURE__*/React.createElement("span", null, "\u2013\xA0\xA0\u2013")
28353
+ }, {
28354
+ value: ".",
28355
+ content: /*#__PURE__*/React.createElement("span", null, "\xB7\xB7\xB7\xB7")
28356
+ }, {
28357
+ value: ". ",
28358
+ content: /*#__PURE__*/React.createElement("span", null, "\xB7 \xB7 \xB7")
28359
+ }],
28360
+ onChange: this.props.onChange
28361
+ });
28362
+ }
28363
+ }
28364
+ DashPicker.defaultProps = {
28365
+ value: ""
28366
+ };
28367
+
28261
28368
  /**
28262
28369
  * A simple Mathquill MathField input for the InteractionEditor.
28263
28370
  * Unlike our other uses of Mathquill, we're not using a keypad here;
@@ -28303,13 +28410,13 @@ function MathquillInput(props) {
28303
28410
  }
28304
28411
  });
28305
28412
  return /*#__PURE__*/React.createElement(View, {
28306
- style: styles$J.outerWrapper
28413
+ style: styles$K.outerWrapper
28307
28414
  }, /*#__PURE__*/React.createElement("span", {
28308
28415
  ref: mathFieldWrapperRef,
28309
28416
  className: "perseus-math-input mq-editable-field mq-math-mode"
28310
28417
  }));
28311
28418
  }
28312
- const styles$J = StyleSheet.create({
28419
+ const styles$K = StyleSheet.create({
28313
28420
  outerWrapper: {
28314
28421
  display: "inline-block",
28315
28422
  borderStyle: "solid",
@@ -28441,6 +28548,35 @@ LabelEditor.defaultProps = {
28441
28548
  label: "\\phi"
28442
28549
  };
28443
28550
 
28551
+ const {
28552
+ ButtonGroup: ButtonGroup$4
28553
+ } = components;
28554
+ class ArrowPicker extends React.Component {
28555
+ render() {
28556
+ return /*#__PURE__*/React.createElement(ButtonGroup$4, {
28557
+ value: this.props.value,
28558
+ allowEmpty: false,
28559
+ buttons: [{
28560
+ value: "",
28561
+ content: /*#__PURE__*/React.createElement("span", null, "\u2014")
28562
+ }, {
28563
+ value: "->",
28564
+ content: /*#__PURE__*/React.createElement("span", null, "\u2192")
28565
+ }
28566
+ /*
28567
+ TODO(eater): fix khan-exercises so these are supported
28568
+ {value: "<-", content: <span>&#x2190;</span>},
28569
+ {value: "<->", content: <span>&#x2194;</span>}
28570
+ */],
28571
+
28572
+ onChange: this.props.onChange
28573
+ });
28574
+ }
28575
+ }
28576
+ ArrowPicker.defaultProps = {
28577
+ value: ""
28578
+ };
28579
+
28444
28580
  const {
28445
28581
  NumberInput: NumberInput$c
28446
28582
  } = components;
@@ -29346,6 +29482,20 @@ class InteractionEditor extends React.Component {
29346
29482
  InteractionEditor.widgetName = "interaction";
29347
29483
  InteractionEditor.defaultProps = defaultInteractionProps;
29348
29484
 
29485
+ var isProduction = process.env.NODE_ENV === 'production';
29486
+ var prefix = 'Invariant failed';
29487
+ function invariant(condition, message) {
29488
+ if (condition) {
29489
+ return;
29490
+ }
29491
+ if (isProduction) {
29492
+ throw new Error(prefix);
29493
+ }
29494
+ var provided = typeof message === 'function' ? message() : message;
29495
+ var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
29496
+ throw new Error(value);
29497
+ }
29498
+
29349
29499
  // Renders the given children in a View laid out horizontally.
29350
29500
  const LabeledRow = props => {
29351
29501
  const {
@@ -29355,16 +29505,16 @@ const LabeledRow = props => {
29355
29505
  style
29356
29506
  } = props;
29357
29507
  return /*#__PURE__*/React.createElement("label", {
29358
- className: css(styles$I.label)
29508
+ className: css(styles$J.label)
29359
29509
  }, /*#__PURE__*/React.createElement(View, {
29360
- style: [styles$I.row, style]
29510
+ style: [styles$J.row, style]
29361
29511
  }, labelSide === "start" || /*#__PURE__*/React.createElement(LabelSmall, {
29362
- style: styles$I.spaceEnd
29512
+ style: styles$J.spaceEnd
29363
29513
  }, label), children, labelSide === "end" && /*#__PURE__*/React.createElement(LabelSmall, {
29364
- style: styles$I.spaceStart
29514
+ style: styles$J.spaceStart
29365
29515
  }, label)));
29366
29516
  };
29367
- const styles$I = StyleSheet.create({
29517
+ const styles$J = StyleSheet.create({
29368
29518
  label: {
29369
29519
  width: "fit-content"
29370
29520
  },
@@ -29389,11 +29539,11 @@ function Heading({
29389
29539
  onToggle
29390
29540
  }) {
29391
29541
  return /*#__PURE__*/React.createElement(Clickable, {
29392
- style: [styles$H.container, !isCollapsible && styles$H.notClickable],
29542
+ style: [styles$I.container, !isCollapsible && styles$I.notClickable],
29393
29543
  disabled: !isCollapsible,
29394
29544
  onClick: () => isCollapsible && (onToggle == null ? void 0 : onToggle(!isOpen))
29395
29545
  }, () => /*#__PURE__*/React.createElement(View, {
29396
- style: styles$H.heading
29546
+ style: styles$I.heading
29397
29547
  }, /*#__PURE__*/React.createElement(LabelLarge, {
29398
29548
  style: {
29399
29549
  fontSize: 14,
@@ -29403,7 +29553,7 @@ function Heading({
29403
29553
  isExpanded: isOpen
29404
29554
  })));
29405
29555
  }
29406
- const styles$H = StyleSheet.create({
29556
+ const styles$I = StyleSheet.create({
29407
29557
  container: {
29408
29558
  marginTop: spacing.small_12,
29409
29559
  // NOTE(jeremy): This is the inverse of the @editorPadding CSS
@@ -29630,35 +29780,20 @@ const getAngleEquation = startCoords => {
29630
29780
  const roundedAngle = angle.toFixed(0);
29631
29781
  return `${roundedAngle}\u00B0 angle at (${vertex[0]}, ${vertex[1]})`;
29632
29782
  };
29633
- const shouldShowStartCoordsUI = (flags, graph) => {
29634
- var _flags$mafs, _flags$mafs2, _flags$mafs3, _flags$mafs4, _flags$mafs5;
29635
- // TODO(LEMS-2228): Remove flags once this is fully released
29636
- const startCoordsUiPhase1Types = ["linear", "linear-system", "ray", "segment", "circle"];
29637
- const startCoordsUiPhase2Types = ["sinusoid", "quadratic"];
29638
- const startCoordsPhase1 = flags == null || (_flags$mafs = flags.mafs) == null ? void 0 : _flags$mafs["start-coords-ui-phase-1"];
29639
- const startCoordsPhase2 = flags == null || (_flags$mafs2 = flags.mafs) == null ? void 0 : _flags$mafs2["start-coords-ui-phase-2"];
29640
- const startCoordsPoint = flags == null || (_flags$mafs3 = flags.mafs) == null ? void 0 : _flags$mafs3["start-coords-ui-point"];
29641
- const startCoordsPolygon = flags == null || (_flags$mafs4 = flags.mafs) == null ? void 0 : _flags$mafs4["start-coords-ui-polygon"];
29642
- const startCoordsAngle = flags == null || (_flags$mafs5 = flags.mafs) == null ? void 0 : _flags$mafs5["start-coords-ui-angle"];
29643
- if (startCoordsPhase1 && startCoordsUiPhase1Types.includes(graph.type)) {
29644
- return true;
29645
- }
29646
- if (startCoordsPhase2 && startCoordsUiPhase2Types.includes(graph.type)) {
29647
- return true;
29648
- }
29649
- if (startCoordsAngle && graph.type === "angle") {
29650
- return true;
29783
+ const shouldShowStartCoordsUI = (graph, isStatic) => {
29784
+ // We don't show the start coords UI for static graphs, since
29785
+ // the coords shown for this are determined by the coords on the
29786
+ // "correct" graph. The start coords would not be used here.
29787
+ if (isStatic) {
29788
+ return false;
29651
29789
  }
29652
- if (startCoordsPoint && graph.type === "point" && graph.numPoints !== "unlimited") {
29653
- return true;
29790
+ if (graph.type === "point" && graph.numPoints === "unlimited") {
29791
+ return false;
29654
29792
  }
29655
- if (startCoordsPolygon && graph.type === "polygon" && graph.numSides !== "unlimited" && (
29656
- // Pre-initialized graph with undefined snapTo value
29657
- // initializes to snapTo="grid"
29658
- graph.snapTo === "grid" || graph.snapTo === undefined)) {
29659
- return true;
29793
+ if (graph.type === "polygon" && (graph.numSides === "unlimited" || graph.snapTo === "angles" || graph.snapTo === "sides")) {
29794
+ return false;
29660
29795
  }
29661
- return false;
29796
+ return true;
29662
29797
  };
29663
29798
 
29664
29799
  /**
@@ -29673,25 +29808,22 @@ const LockedFigureSelect = props => {
29673
29808
  id,
29674
29809
  onChange
29675
29810
  } = props;
29676
- const figureTypes = ["point", "line", "vector", "ellipse", "polygon"];
29677
- if (props.showM2bFeatures) {
29678
- figureTypes.push("function");
29679
- }
29811
+ const figureTypes = ["point", "line", "vector", "ellipse", "polygon", "function"];
29680
29812
  if (props.showLabelsFlag) {
29681
29813
  figureTypes.push("label");
29682
29814
  }
29683
29815
  return /*#__PURE__*/React.createElement(View, {
29684
- style: styles$G.container
29816
+ style: styles$H.container
29685
29817
  }, /*#__PURE__*/React.createElement(ActionMenu, {
29686
29818
  menuText: "Add locked figure",
29687
- style: styles$G.addElementSelect
29819
+ style: styles$H.addElementSelect
29688
29820
  }, figureTypes.map(figureType => /*#__PURE__*/React.createElement(ActionItem, {
29689
29821
  key: `${id}-${figureType}`,
29690
29822
  label: figureType,
29691
29823
  onClick: () => onChange(figureType)
29692
29824
  }))));
29693
29825
  };
29694
- const styles$G = StyleSheet.create({
29826
+ const styles$H = StyleSheet.create({
29695
29827
  container: {
29696
29828
  marginTop: spacing.xSmall_8
29697
29829
  },
@@ -29761,18 +29893,18 @@ const AngleInput = props => {
29761
29893
  }
29762
29894
  return /*#__PURE__*/React.createElement(LabelMedium, {
29763
29895
  tag: "label",
29764
- style: styles$F.row
29896
+ style: styles$G.row
29765
29897
  }, "angle (degrees)", /*#__PURE__*/React.createElement(Strut, {
29766
29898
  size: spacing.xxSmall_6
29767
29899
  }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29768
29900
  value: angleInput,
29769
29901
  onChange: handleAngleChange,
29770
- style: styles$F.textField
29902
+ style: styles$G.textField
29771
29903
  }), /*#__PURE__*/React.createElement(Strut, {
29772
29904
  size: spacing.xxSmall_6
29773
29905
  }));
29774
29906
  };
29775
- const styles$F = StyleSheet.create({
29907
+ const styles$G = StyleSheet.create({
29776
29908
  row: {
29777
29909
  display: "flex",
29778
29910
  flexDirection: "row",
@@ -29821,30 +29953,30 @@ const CoordinatePairInput = props => {
29821
29953
  onChange(newCoords);
29822
29954
  }
29823
29955
  return /*#__PURE__*/React.createElement(View, {
29824
- style: [styles$E.row, style]
29956
+ style: [styles$F.row, style]
29825
29957
  }, /*#__PURE__*/React.createElement(LabelMedium, {
29826
29958
  tag: "label",
29827
- style: styles$E.row
29959
+ style: styles$F.row
29828
29960
  }, labels ? labels[0] : "x coord", /*#__PURE__*/React.createElement(Strut, {
29829
29961
  size: spacing.xxSmall_6
29830
29962
  }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29831
29963
  value: coordState[0],
29832
29964
  onChange: newValue => handleCoordChange(newValue, 0),
29833
- style: [styles$E.textField, error ? styles$E.errorField : undefined]
29965
+ style: [styles$F.textField, error ? styles$F.errorField : undefined]
29834
29966
  })), /*#__PURE__*/React.createElement(Strut, {
29835
29967
  size: spacing.medium_16
29836
29968
  }), /*#__PURE__*/React.createElement(LabelMedium, {
29837
29969
  tag: "label",
29838
- style: styles$E.row
29970
+ style: styles$F.row
29839
29971
  }, labels ? labels[1] : "y coord", /*#__PURE__*/React.createElement(Strut, {
29840
29972
  size: spacing.xxSmall_6
29841
29973
  }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29842
29974
  value: coordState[1],
29843
29975
  onChange: newValue => handleCoordChange(newValue, 1),
29844
- style: [styles$E.textField, error ? styles$E.errorField : undefined]
29976
+ style: [styles$F.textField, error ? styles$F.errorField : undefined]
29845
29977
  })));
29846
29978
  };
29847
- const styles$E = StyleSheet.create({
29979
+ const styles$F = StyleSheet.create({
29848
29980
  row: {
29849
29981
  display: "flex",
29850
29982
  flexDirection: "row",
@@ -29877,14 +30009,14 @@ const PerseusEditorAccordion = props => {
29877
30009
  }, /*#__PURE__*/React.createElement(AccordionSection, {
29878
30010
  expanded: expanded,
29879
30011
  onToggle: onToggle,
29880
- style: [styles$D.container, containerStyle],
29881
- headerStyle: styles$D.accordionHeader,
30012
+ style: [styles$E.container, containerStyle],
30013
+ headerStyle: styles$E.accordionHeader,
29882
30014
  header: header
29883
30015
  }, /*#__PURE__*/React.createElement(View, {
29884
- style: [styles$D.accordionPanel, panelStyle]
30016
+ style: [styles$E.accordionPanel, panelStyle]
29885
30017
  }, children)));
29886
30018
  };
29887
- const styles$D = StyleSheet.create({
30019
+ const styles$E = StyleSheet.create({
29888
30020
  container: {
29889
30021
  backgroundColor: color.fadedBlue8,
29890
30022
  marginTop: spacing.xSmall_8
@@ -29915,13 +30047,13 @@ const ColorSwatch = props => {
29915
30047
  "aria-label":
29916
30048
  // Don't include an aria label for decorative circles.
29917
30049
  !decorative ? `${color$1}, ${filled ? "filled" : "open"}` : undefined,
29918
- style: [styles$C.colorSwatch, {
30050
+ style: [styles$D.colorSwatch, {
29919
30051
  border: `4px solid ${lockedFigureColors[color$1]}`,
29920
30052
  backgroundColor: filled ? lockedFigureColors[color$1] : color.white
29921
30053
  }]
29922
30054
  });
29923
30055
  };
29924
- const styles$C = StyleSheet.create({
30056
+ const styles$D = StyleSheet.create({
29925
30057
  colorSwatch: {
29926
30058
  // Add a white outline so that the color swatch is visible when
29927
30059
  // the dropdown option is highlighted with its blue background.
@@ -29940,10 +30072,10 @@ const ColorSelect = props => {
29940
30072
  onChange
29941
30073
  } = props;
29942
30074
  return /*#__PURE__*/React.createElement(View, {
29943
- style: [styles$B.row, style]
30075
+ style: [styles$C.row, style]
29944
30076
  }, /*#__PURE__*/React.createElement(LabelMedium, {
29945
30077
  tag: "label",
29946
- style: styles$B.row
30078
+ style: styles$C.row
29947
30079
  }, "color", /*#__PURE__*/React.createElement(Strut, {
29948
30080
  size: spacing.xxSmall_6
29949
30081
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -29962,7 +30094,7 @@ const ColorSelect = props => {
29962
30094
  })
29963
30095
  })))));
29964
30096
  };
29965
- const styles$B = StyleSheet.create({
30097
+ const styles$C = StyleSheet.create({
29966
30098
  row: {
29967
30099
  display: "flex",
29968
30100
  flexDirection: "row",
@@ -29981,17 +30113,17 @@ const EllipseSwatch = props => {
29981
30113
  } = props;
29982
30114
  return /*#__PURE__*/React.createElement(View, {
29983
30115
  "aria-label": `${color}, stroke ${strokeStyle}, fill ${fillStyle}`,
29984
- style: [styles$A.container, {
30116
+ style: [styles$B.container, {
29985
30117
  border: `4px ${strokeStyle} ${lockedFigureColors[color]}`
29986
30118
  }]
29987
30119
  }, /*#__PURE__*/React.createElement(View, {
29988
- style: [styles$A.innerCircle, {
30120
+ style: [styles$B.innerCircle, {
29989
30121
  backgroundColor: lockedFigureColors[color],
29990
30122
  opacity: fillStyle === "white" ? 0 : lockedFigureFillStyles[fillStyle]
29991
30123
  }]
29992
30124
  }));
29993
30125
  };
29994
- const styles$A = StyleSheet.create({
30126
+ const styles$B = StyleSheet.create({
29995
30127
  container: {
29996
30128
  // Add a white outline so that the color swatch is visible when
29997
30129
  // the dropdown option is highlighted with its blue background.
@@ -30017,7 +30149,7 @@ const LineStrokeSelect = props => {
30017
30149
  } = props;
30018
30150
  return /*#__PURE__*/React.createElement(LabelMedium, {
30019
30151
  tag: "label",
30020
- style: styles$z.lineStrokeSelect
30152
+ style: styles$A.lineStrokeSelect
30021
30153
  }, "stroke", /*#__PURE__*/React.createElement(Strut, {
30022
30154
  size: spacing.xxxSmall_4
30023
30155
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30034,7 +30166,7 @@ const LineStrokeSelect = props => {
30034
30166
  label: "dashed"
30035
30167
  })));
30036
30168
  };
30037
- const styles$z = StyleSheet.create({
30169
+ const styles$A = StyleSheet.create({
30038
30170
  lineStrokeSelect: {
30039
30171
  display: "flex",
30040
30172
  flexDirection: "row",
@@ -30056,40 +30188,40 @@ const LockedFigureSettingsActions = props => {
30056
30188
  onRemove
30057
30189
  } = props;
30058
30190
  return /*#__PURE__*/React.createElement(View, {
30059
- style: styles$y.container
30191
+ style: styles$z.container
30060
30192
  }, /*#__PURE__*/React.createElement(Button, {
30061
30193
  startIcon: trashIcon,
30062
30194
  "aria-label": `Delete locked ${figureType}`,
30063
30195
  onClick: onRemove,
30064
30196
  kind: "tertiary",
30065
- style: styles$y.deleteButton
30197
+ style: styles$z.deleteButton
30066
30198
  }, "Delete"), /*#__PURE__*/React.createElement(Spring, null), /*#__PURE__*/React.createElement(IconButton, {
30067
30199
  icon: caretDoubleUpIcon,
30068
30200
  size: "small",
30069
30201
  "aria-label": `Move locked ${figureType} to the back`,
30070
30202
  onClick: () => onMove("back"),
30071
- style: styles$y.iconButton
30203
+ style: styles$z.iconButton
30072
30204
  }), /*#__PURE__*/React.createElement(IconButton, {
30073
30205
  icon: caretUpIcon,
30074
30206
  size: "small",
30075
30207
  "aria-label": `Move locked ${figureType} backward`,
30076
30208
  onClick: () => onMove("backward"),
30077
- style: styles$y.iconButton
30209
+ style: styles$z.iconButton
30078
30210
  }), /*#__PURE__*/React.createElement(IconButton, {
30079
30211
  icon: caretDownIcon,
30080
30212
  size: "small",
30081
30213
  "aria-label": `Move locked ${figureType} forward`,
30082
30214
  onClick: () => onMove("forward"),
30083
- style: styles$y.iconButton
30215
+ style: styles$z.iconButton
30084
30216
  }), /*#__PURE__*/React.createElement(IconButton, {
30085
30217
  icon: caretDoubleDownIcon,
30086
30218
  size: "small",
30087
30219
  "aria-label": `Move locked ${figureType} to the front`,
30088
30220
  onClick: () => onMove("front"),
30089
- style: styles$y.iconButton
30221
+ style: styles$z.iconButton
30090
30222
  }));
30091
30223
  };
30092
- const styles$y = StyleSheet.create({
30224
+ const styles$z = StyleSheet.create({
30093
30225
  container: {
30094
30226
  width: "100%",
30095
30227
  flexDirection: "row",
@@ -30136,7 +30268,7 @@ const LockedEllipseSettings = props => {
30136
30268
  /*#__PURE__*/
30137
30269
  // Summary: Ellipse, center, radius, color (opacity, dashed)
30138
30270
  React.createElement(View, {
30139
- style: styles$x.row
30271
+ style: styles$y.row
30140
30272
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Ellipse (${center[0]}, ${center[1]}), radius ${radius[0]}, ${radius[1]}`), /*#__PURE__*/React.createElement(Strut, {
30141
30273
  size: spacing.xSmall_8
30142
30274
  }), /*#__PURE__*/React.createElement(EllipseSwatch, {
@@ -30145,19 +30277,19 @@ const LockedEllipseSettings = props => {
30145
30277
  strokeStyle: strokeStyle
30146
30278
  }))
30147
30279
  }, /*#__PURE__*/React.createElement(View, {
30148
- style: styles$x.row
30280
+ style: styles$y.row
30149
30281
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30150
30282
  coord: center,
30151
- style: styles$x.spaceUnder,
30283
+ style: styles$y.spaceUnder,
30152
30284
  onChange: newCoords => onChangeProps({
30153
30285
  center: newCoords
30154
30286
  })
30155
30287
  }), /*#__PURE__*/React.createElement(View, {
30156
- style: styles$x.spaceUnder
30288
+ style: styles$y.spaceUnder
30157
30289
  }, /*#__PURE__*/React.createElement(InfoTip$d, null, "The coordinates for the center of the ellipse."))), /*#__PURE__*/React.createElement(CoordinatePairInput, {
30158
30290
  coord: radius,
30159
30291
  labels: ["x radius", "y radius"],
30160
- style: styles$x.spaceUnder,
30292
+ style: styles$y.spaceUnder,
30161
30293
  onChange: newCoords => onChangeProps({
30162
30294
  radius: newCoords
30163
30295
  })
@@ -30169,7 +30301,7 @@ const LockedEllipseSettings = props => {
30169
30301
  }), /*#__PURE__*/React.createElement(Strut, {
30170
30302
  size: spacing.xSmall_8
30171
30303
  }), /*#__PURE__*/React.createElement(View, {
30172
- style: [styles$x.row, styles$x.spaceUnder]
30304
+ style: [styles$y.row, styles$y.spaceUnder]
30173
30305
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30174
30306
  selectedValue: color,
30175
30307
  onChange: handleColorChange
@@ -30177,7 +30309,7 @@ const LockedEllipseSettings = props => {
30177
30309
  size: spacing.medium_16
30178
30310
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30179
30311
  tag: "label",
30180
- style: [styles$x.row, styles$x.truncatedWidth]
30312
+ style: [styles$y.row, styles$y.truncatedWidth]
30181
30313
  }, "fill", /*#__PURE__*/React.createElement(Strut, {
30182
30314
  size: spacing.xxSmall_6
30183
30315
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30203,7 +30335,7 @@ const LockedEllipseSettings = props => {
30203
30335
  onRemove: onRemove
30204
30336
  }));
30205
30337
  };
30206
- const styles$x = StyleSheet.create({
30338
+ const styles$y = StyleSheet.create({
30207
30339
  row: {
30208
30340
  display: "flex",
30209
30341
  flexDirection: "row",
@@ -30224,15 +30356,15 @@ const LineSwatch = props => {
30224
30356
  lineStyle
30225
30357
  } = props;
30226
30358
  return /*#__PURE__*/React.createElement(View, {
30227
- style: styles$w.container
30359
+ style: styles$x.container
30228
30360
  }, /*#__PURE__*/React.createElement(View, {
30229
30361
  "aria-label": `${color}, ${lineStyle}`,
30230
- style: [styles$w.lineSwatch, {
30362
+ style: [styles$x.lineSwatch, {
30231
30363
  border: `5px ${lineStyle} ${lockedFigureColors[color]}`
30232
30364
  }]
30233
30365
  }));
30234
30366
  };
30235
- const styles$w = StyleSheet.create({
30367
+ const styles$x = StyleSheet.create({
30236
30368
  container: {
30237
30369
  backgroundColor: color.white,
30238
30370
  justifyContent: "center",
@@ -30244,6 +30376,12 @@ const styles$w = StyleSheet.create({
30244
30376
  }
30245
30377
  });
30246
30378
 
30379
+ const examples = {
30380
+ linear: ["x + 5", "1/2x - 2"],
30381
+ polynomial: ["1/2x^2 + 3x - 4", "(1/3)x^3 - 2x^2 + 3x - 4"],
30382
+ trigonometric: ["sin(x) * 3", "arctan(2x) + 4"]
30383
+ };
30384
+
30247
30385
  /**
30248
30386
  * LockedFunctionSettings is a component that allows the user to edit the
30249
30387
  * settings of specifically a locked function on the graph.
@@ -30262,18 +30400,20 @@ const LockedFunctionSettings = props => {
30262
30400
  onRemove
30263
30401
  } = props;
30264
30402
  const equationPrefix = directionalAxis === "x" ? "y=" : "x=";
30403
+ const domainRangeText = directionalAxis === "x" ? "domain" : "range";
30265
30404
  const lineLabel = `Function (${equationPrefix}${equation})`;
30266
30405
 
30267
- // Tracking the string value of domain constraints to handle interim state of
30406
+ // Tracking the string value of domain/range constraints to handle interim state of
30268
30407
  // entering a negative value, as well as representing Infinity as an empty string.
30269
30408
  // This variable is used when specifying the values of the input fields.
30270
30409
  const [domainEntries, setDomainEntries] = useState([domain && domain[0] !== -Infinity ? domain[0].toString() : "", domain && domain[1] !== Infinity ? domain[1].toString() : ""]);
30410
+ const [exampleCategory, setExampleCategory] = useState("");
30271
30411
  useEffect(() => {
30272
- // "useEffect" used to maintain parity between domain constraints and their string representation.
30412
+ // "useEffect" used to maintain parity between domain/range constraints and their string representation.
30273
30413
  setDomainEntries([domain && domain[0] !== -Infinity ? domain[0].toString() : "", domain && domain[1] !== Infinity ? domain[1].toString() : ""]);
30274
30414
  }, [domain]);
30275
30415
 
30276
- // Generic function for handling property changes (except for 'domain')
30416
+ // Generic function for handling property changes (except for 'domain/range')
30277
30417
  function handlePropChange(property, newValue) {
30278
30418
  const updatedProps = {};
30279
30419
  updatedProps[property] = newValue;
@@ -30281,8 +30421,8 @@ const LockedFunctionSettings = props => {
30281
30421
  }
30282
30422
 
30283
30423
  /*
30284
- Reason for having a separate 'propChange' function for 'domain':
30285
- Domain entries are optional. Their default value is +/- Infinity.
30424
+ Reason for having a separate 'propChange' function for 'domain/range':
30425
+ Domain/Range entries are optional. Their default value is +/- Infinity.
30286
30426
  Since input fields that are empty evaluate to zero, there needs to be
30287
30427
  dedicated code to convert empty to Infinity.
30288
30428
  */
@@ -30302,13 +30442,16 @@ const LockedFunctionSettings = props => {
30302
30442
  domain: newDomain
30303
30443
  });
30304
30444
  }
30445
+ const exampleCategories = Object.keys(examples);
30446
+ const exampleCategorySelected = exampleCategory !== "";
30447
+ const exampleContent = exampleCategorySelected ? examples[exampleCategory] : ["Select category to see example equations"];
30305
30448
  return /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
30306
30449
  expanded: props.expanded,
30307
30450
  onToggle: props.onToggle,
30308
30451
  header: /*#__PURE__*/React.createElement(View, {
30309
- style: styles$v.row
30452
+ style: styles$w.row
30310
30453
  }, /*#__PURE__*/React.createElement(LabelLarge, {
30311
- style: styles$v.accordionHeader
30454
+ style: styles$w.accordionHeader
30312
30455
  }, lineLabel), /*#__PURE__*/React.createElement(Strut, {
30313
30456
  size: spacing.xSmall_8
30314
30457
  }), /*#__PURE__*/React.createElement(LineSwatch, {
@@ -30316,7 +30459,7 @@ const LockedFunctionSettings = props => {
30316
30459
  lineStyle: strokeStyle
30317
30460
  }))
30318
30461
  }, /*#__PURE__*/React.createElement(View, {
30319
- style: [styles$v.row, styles$v.spaceUnder]
30462
+ style: [styles$w.row, styles$w.spaceUnder]
30320
30463
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30321
30464
  selectedValue: lineColor,
30322
30465
  onChange: newValue => {
@@ -30330,14 +30473,14 @@ const LockedFunctionSettings = props => {
30330
30473
  handlePropChange("strokeStyle", newValue);
30331
30474
  }
30332
30475
  })), /*#__PURE__*/React.createElement(View, {
30333
- style: [styles$v.row, styles$v.rowSpace]
30476
+ style: [styles$w.row, styles$w.rowSpace]
30334
30477
  }, /*#__PURE__*/React.createElement(SingleSelect, {
30335
30478
  selectedValue: directionalAxis,
30336
30479
  onChange: newValue => {
30337
30480
  handlePropChange("directionalAxis", newValue);
30338
30481
  },
30339
30482
  "aria-label": "equation prefix",
30340
- style: styles$v.equationPrefix
30483
+ style: [styles$w.dropdownLabel, styles$w.axisMenu]
30341
30484
  // Placeholder is required, but never gets used.
30342
30485
  ,
30343
30486
  placeholder: ""
@@ -30356,17 +30499,17 @@ const LockedFunctionSettings = props => {
30356
30499
  onChange: newValue => {
30357
30500
  handlePropChange("equation", newValue);
30358
30501
  },
30359
- style: [styles$v.textField]
30502
+ style: [styles$w.textField]
30360
30503
  })), /*#__PURE__*/React.createElement(View, {
30361
- style: [styles$v.row, styles$v.rowSpace]
30504
+ style: [styles$w.row, styles$w.rowSpace]
30362
30505
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30363
30506
  tag: "label",
30364
- style: styles$v.domainMin
30365
- }, "domain min", /*#__PURE__*/React.createElement(Strut, {
30507
+ style: [styles$w.dropdownLabel, styles$w.domainMin]
30508
+ }, `${domainRangeText} min`, /*#__PURE__*/React.createElement(Strut, {
30366
30509
  size: spacing.xxSmall_6
30367
30510
  }), /*#__PURE__*/React.createElement(TextField, {
30368
30511
  type: "number",
30369
- style: styles$v.domainMinField,
30512
+ style: styles$w.domainMinField,
30370
30513
  value: domainEntries[0],
30371
30514
  onChange: newValue => {
30372
30515
  handleDomainChange(0, newValue);
@@ -30375,24 +30518,83 @@ const LockedFunctionSettings = props => {
30375
30518
  size: spacing.medium_16
30376
30519
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30377
30520
  tag: "label",
30378
- "aria-label": "domain max",
30379
- style: styles$v.domainMax
30521
+ "aria-label": `${domainRangeText} max`,
30522
+ style: [styles$w.dropdownLabel, styles$w.domainMax]
30380
30523
  }, "max", /*#__PURE__*/React.createElement(Strut, {
30381
30524
  size: spacing.xxSmall_6
30382
30525
  }), /*#__PURE__*/React.createElement(TextField, {
30383
30526
  type: "number",
30384
- style: styles$v.domainMaxField,
30527
+ style: styles$w.domainMaxField,
30385
30528
  value: domainEntries[1],
30386
30529
  onChange: newValue => {
30387
30530
  handleDomainChange(1, newValue);
30388
30531
  }
30389
- }))), /*#__PURE__*/React.createElement(LockedFigureSettingsActions, {
30532
+ }))), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
30533
+ header: /*#__PURE__*/React.createElement(LabelLarge, null, "Example Functions"),
30534
+ expanded: false,
30535
+ containerStyle: styles$w.exampleWorkspace,
30536
+ panelStyle: styles$w.exampleAccordionPanel
30537
+ }, /*#__PURE__*/React.createElement(LabelMedium, {
30538
+ tag: "label",
30539
+ style: styles$w.dropdownLabel
30540
+ }, "Choose a category", /*#__PURE__*/React.createElement(Strut, {
30541
+ size: spacing.xxSmall_6
30542
+ }), /*#__PURE__*/React.createElement(SingleSelect, {
30543
+ selectedValue: exampleCategory,
30544
+ onChange: setExampleCategory,
30545
+ placeholder: "examples"
30546
+ }, exampleCategories.map(category => {
30547
+ return /*#__PURE__*/React.createElement(OptionItem, {
30548
+ key: category,
30549
+ value: category,
30550
+ label: category
30551
+ });
30552
+ }))), exampleCategorySelected && /*#__PURE__*/React.createElement("ul", {
30553
+ className: css(styles$w.exampleContainer)
30554
+ }, exampleContent.map((example, index) => /*#__PURE__*/React.createElement(ExampleItem, {
30555
+ category: exampleCategory,
30556
+ example: example,
30557
+ index: index,
30558
+ pasteEquationFn: handlePropChange
30559
+ })))), /*#__PURE__*/React.createElement(LockedFigureSettingsActions, {
30390
30560
  figureType: props.type,
30391
30561
  onMove: onMove,
30392
30562
  onRemove: onRemove
30393
30563
  }));
30394
30564
  };
30395
- const styles$v = StyleSheet.create({
30565
+ const ExampleItem = props => {
30566
+ const {
30567
+ category,
30568
+ example,
30569
+ index,
30570
+ pasteEquationFn
30571
+ } = props;
30572
+ const exampleId = useId();
30573
+ return /*#__PURE__*/React.createElement("li", {
30574
+ key: `${category}-${index}`,
30575
+ className: css(styles$w.exampleRow)
30576
+ }, /*#__PURE__*/React.createElement(IconButton, {
30577
+ icon: autoPasteIcon,
30578
+ "aria-label": "paste example",
30579
+ "aria-describedby": exampleId,
30580
+ onClick: () => pasteEquationFn("equation", example),
30581
+ size: "medium",
30582
+ style: styles$w.copyPasteButton
30583
+ }), /*#__PURE__*/React.createElement(IconButton, {
30584
+ icon: copyIcon,
30585
+ "aria-label": "copy example",
30586
+ "aria-describedby": exampleId,
30587
+ onClick: () => navigator.clipboard.writeText(example),
30588
+ size: "medium",
30589
+ style: styles$w.copyPasteButton
30590
+ }), /*#__PURE__*/React.createElement(Strut, {
30591
+ size: spacing.xxxSmall_4
30592
+ }), /*#__PURE__*/React.createElement(View, {
30593
+ style: styles$w.exampleContent,
30594
+ id: exampleId
30595
+ }, example));
30596
+ };
30597
+ const styles$w = StyleSheet.create({
30396
30598
  accordionHeader: {
30397
30599
  textOverflow: "ellipsis",
30398
30600
  // A maximum width needs to be specified in order for the ellipsis to work.
@@ -30402,17 +30604,20 @@ const styles$v = StyleSheet.create({
30402
30604
  overflow: "hidden",
30403
30605
  whiteSpace: "nowrap"
30404
30606
  },
30405
- equationPrefix: {
30607
+ axisMenu: {
30406
30608
  minWidth: "auto"
30407
30609
  },
30610
+ copyPasteButton: {
30611
+ flexShrink: "0",
30612
+ margin: "0 2px"
30613
+ },
30408
30614
  domainMin: {
30409
- alignItems: "center",
30410
- display: "flex",
30615
+ justifyContent: "space-between",
30411
30616
  // The 'width' property is applied to the label, which wraps the text and the input field.
30412
30617
  // The width of the input fields (min/max) should be the same (to have a consistent look),
30413
30618
  // so the following calculation distributes the space accordingly.
30414
- // For the "domain min" block, the text is 82.7px, and the strut is 6px (88.7px total).
30415
- // The "domain max" block is 30.23px, and the strut is 6px (36.23px total).
30619
+ // For the "domain/range min" block, the text is 82.7px, and the strut is 6px (88.7px total).
30620
+ // The "domain/range max" block is 30.23px, and the strut is 6px (36.23px total).
30416
30621
  // The calculation takes the remain space after the text & struts (141px total) are removed,
30417
30622
  // and divides it between the two input fields equally.
30418
30623
  // The calculation reads: "Take 1/2 of the non-text space, and add the required space for this label's text"
@@ -30425,8 +30630,6 @@ const styles$v = StyleSheet.create({
30425
30630
  },
30426
30631
 
30427
30632
  domainMax: {
30428
- alignItems: "center",
30429
- display: "flex",
30430
30633
  // See explanation for "domainMin" for the calculation below.
30431
30634
  width: "calc(((100% - 141px) / 2) + 36.2px)"
30432
30635
  },
@@ -30434,6 +30637,44 @@ const styles$v = StyleSheet.create({
30434
30637
  width: "calc(100% - 36.2px)" // make room for the label
30435
30638
  },
30436
30639
 
30640
+ dropdownLabel: {
30641
+ alignItems: "center",
30642
+ display: "flex"
30643
+ },
30644
+ exampleAccordionPanel: {
30645
+ alignItems: "start",
30646
+ paddingBottom: "12px",
30647
+ flexDirection: "row",
30648
+ flexWrap: "wrap"
30649
+ },
30650
+ exampleContainer: {
30651
+ background: "white",
30652
+ border: `1px solid ${color.fadedOffBlack16}`,
30653
+ borderRadius: "4px",
30654
+ flexGrow: "1",
30655
+ listStyleType: "none",
30656
+ // Nothing special about the maxHeight value,
30657
+ // just a good height to partially show a 3rd example in the list
30658
+ // to hint at scrollable content.
30659
+ maxHeight: "88px",
30660
+ margin: "8px 0 0 0",
30661
+ overflowY: "scroll",
30662
+ padding: "4px 12px 4px 4px"
30663
+ },
30664
+ exampleContent: {
30665
+ fontFamily: `"Lato", sans-serif`,
30666
+ flexGrow: "1",
30667
+ color: color.offBlack
30668
+ },
30669
+ exampleRow: {
30670
+ alignItems: "center",
30671
+ display: "flex",
30672
+ flexDirection: "row",
30673
+ minHeight: "44px"
30674
+ },
30675
+ exampleWorkspace: {
30676
+ background: color.white50
30677
+ },
30437
30678
  rowSpace: {
30438
30679
  marginTop: spacing.xSmall_8
30439
30680
  },
@@ -30471,14 +30712,14 @@ function LockedLabelSettings(props) {
30471
30712
  expanded: expanded,
30472
30713
  onToggle: onToggle,
30473
30714
  header: /*#__PURE__*/React.createElement(View, {
30474
- style: [styles$u.row, styles$u.accordionHeaderContainer]
30715
+ style: [styles$v.row, styles$v.accordionHeaderContainer]
30475
30716
  }, /*#__PURE__*/React.createElement(LabelLarge, null, "Label (", coord[0], ", ", coord[1], ")"), /*#__PURE__*/React.createElement(Strut, {
30476
30717
  size: spacing.xSmall_8
30477
30718
  }), text !== "" && /*#__PURE__*/React.createElement(LabelLarge, {
30478
30719
  style: [{
30479
30720
  backgroundColor: color.white,
30480
30721
  color: lockedFigureColors[color$1]
30481
- }, styles$u.accordionHeader]
30722
+ }, styles$v.accordionHeader]
30482
30723
  }, text))
30483
30724
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30484
30725
  coord: coord,
@@ -30487,10 +30728,10 @@ function LockedLabelSettings(props) {
30487
30728
  coord: newCoords
30488
30729
  });
30489
30730
  },
30490
- style: styles$u.spaceUnder
30731
+ style: styles$v.spaceUnder
30491
30732
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30492
30733
  tag: "label",
30493
- style: [styles$u.row, styles$u.spaceUnder]
30734
+ style: [styles$v.row, styles$v.spaceUnder]
30494
30735
  }, "TeX", /*#__PURE__*/React.createElement(Strut, {
30495
30736
  size: spacing.xSmall_8
30496
30737
  }), /*#__PURE__*/React.createElement(TextField, {
@@ -30500,7 +30741,7 @@ function LockedLabelSettings(props) {
30500
30741
  text: newValue
30501
30742
  })
30502
30743
  })), /*#__PURE__*/React.createElement(View, {
30503
- style: styles$u.row
30744
+ style: styles$v.row
30504
30745
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30505
30746
  selectedValue: color$1,
30506
30747
  onChange: newColor => {
@@ -30508,12 +30749,12 @@ function LockedLabelSettings(props) {
30508
30749
  color: newColor
30509
30750
  });
30510
30751
  },
30511
- style: styles$u.spaceUnder
30752
+ style: styles$v.spaceUnder
30512
30753
  }), /*#__PURE__*/React.createElement(Strut, {
30513
30754
  size: spacing.medium_16
30514
30755
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30515
30756
  tag: "label",
30516
- style: styles$u.row
30757
+ style: styles$v.row
30517
30758
  }, "size", /*#__PURE__*/React.createElement(Strut, {
30518
30759
  size: spacing.xSmall_8
30519
30760
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30540,7 +30781,7 @@ function LockedLabelSettings(props) {
30540
30781
  onRemove: onRemove
30541
30782
  }));
30542
30783
  }
30543
- const styles$u = StyleSheet.create({
30784
+ const styles$v = StyleSheet.create({
30544
30785
  accordionHeaderContainer: {
30545
30786
  // Stop the label summary from wrapping.
30546
30787
  whiteSpace: "nowrap"
@@ -30574,7 +30815,7 @@ const LabeledSwitch = props => {
30574
30815
  const ids = useUniqueIdWithMock();
30575
30816
  const switchId = ids.get("switch");
30576
30817
  return /*#__PURE__*/React.createElement(View, {
30577
- style: [styles$t.row, style]
30818
+ style: [styles$u.row, style]
30578
30819
  }, /*#__PURE__*/React.createElement(Switch, {
30579
30820
  id: switchId,
30580
30821
  checked: checked,
@@ -30586,7 +30827,7 @@ const LabeledSwitch = props => {
30586
30827
  htmlFor: switchId
30587
30828
  }, label));
30588
30829
  };
30589
- const styles$t = StyleSheet.create({
30830
+ const styles$u = StyleSheet.create({
30590
30831
  row: {
30591
30832
  flexDirection: "row",
30592
30833
  alignItems: "center"
@@ -30620,13 +30861,13 @@ const DefiningPointSettings = props => {
30620
30861
  return /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
30621
30862
  expanded: expanded,
30622
30863
  onToggle: onToggle,
30623
- containerStyle: styles$s.container,
30624
- panelStyle: styles$s.accordionPanel,
30864
+ containerStyle: styles$t.container,
30865
+ panelStyle: styles$t.accordionPanel,
30625
30866
  header:
30626
30867
  /*#__PURE__*/
30627
30868
  // Summary: Point, coords, color (filled/open)
30628
30869
  React.createElement(View, {
30629
- style: styles$s.row
30870
+ style: styles$t.row
30630
30871
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `${label || "Point"} (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React.createElement(Strut, {
30631
30872
  size: spacing.xSmall_8
30632
30873
  }), showPoint && /*#__PURE__*/React.createElement(ColorSwatch, {
@@ -30636,7 +30877,7 @@ const DefiningPointSettings = props => {
30636
30877
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30637
30878
  coord: coord,
30638
30879
  error: !!error,
30639
- style: styles$s.spaceUnder,
30880
+ style: styles$t.spaceUnder,
30640
30881
  onChange: newCoords => {
30641
30882
  onChangeProps({
30642
30883
  coord: newCoords
@@ -30645,12 +30886,12 @@ const DefiningPointSettings = props => {
30645
30886
  }), onTogglePoint && /*#__PURE__*/React.createElement(LabeledSwitch, {
30646
30887
  label: "show point on graph",
30647
30888
  checked: !!showPoint,
30648
- style: showPoint && styles$s.spaceUnder,
30889
+ style: showPoint && styles$t.spaceUnder,
30649
30890
  onChange: onTogglePoint
30650
30891
  }), showPoint && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColorSelect, {
30651
30892
  selectedValue: pointColor,
30652
30893
  onChange: handleColorChange,
30653
- style: styles$s.spaceUnder
30894
+ style: styles$t.spaceUnder
30654
30895
  }), /*#__PURE__*/React.createElement(LabeledSwitch, {
30655
30896
  label: "open point",
30656
30897
  checked: !filled,
@@ -30661,7 +30902,7 @@ const DefiningPointSettings = props => {
30661
30902
  }
30662
30903
  })));
30663
30904
  };
30664
- const styles$s = StyleSheet.create({
30905
+ const styles$t = StyleSheet.create({
30665
30906
  container: {
30666
30907
  marginTop: spacing.xSmall_8,
30667
30908
  marginBottom: 0,
@@ -30724,7 +30965,7 @@ const LockedLineSettings = props => {
30724
30965
  expanded: props.expanded,
30725
30966
  onToggle: props.onToggle,
30726
30967
  header: /*#__PURE__*/React.createElement(View, {
30727
- style: styles$r.row
30968
+ style: styles$s.row
30728
30969
  }, /*#__PURE__*/React.createElement(LabelLarge, null, lineLabel), /*#__PURE__*/React.createElement(Strut, {
30729
30970
  size: spacing.xSmall_8
30730
30971
  }), /*#__PURE__*/React.createElement(LineSwatch, {
@@ -30733,7 +30974,7 @@ const LockedLineSettings = props => {
30733
30974
  }))
30734
30975
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30735
30976
  tag: "label",
30736
- style: [styles$r.row, styles$r.spaceUnder]
30977
+ style: [styles$s.row, styles$s.spaceUnder]
30737
30978
  }, "kind", /*#__PURE__*/React.createElement(Strut, {
30738
30979
  size: spacing.xxxSmall_4
30739
30980
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30754,7 +30995,7 @@ const LockedLineSettings = props => {
30754
30995
  value: "segment",
30755
30996
  label: "segment"
30756
30997
  }))), /*#__PURE__*/React.createElement(View, {
30757
- style: styles$r.row
30998
+ style: styles$s.row
30758
30999
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30759
31000
  selectedValue: lineColor,
30760
31001
  onChange: handleColorChange
@@ -30766,7 +31007,7 @@ const LockedLineSettings = props => {
30766
31007
  lineStyle: value
30767
31008
  })
30768
31009
  })), isInvalid && /*#__PURE__*/React.createElement(LabelMedium, {
30769
- style: styles$r.errorText
31010
+ style: styles$s.errorText
30770
31011
  }, lengthZeroStr), /*#__PURE__*/React.createElement(DefiningPointSettings, _extends({
30771
31012
  label: "Point 1",
30772
31013
  showPoint: showPoint1,
@@ -30791,7 +31032,7 @@ const LockedLineSettings = props => {
30791
31032
  onRemove: onRemove
30792
31033
  }));
30793
31034
  };
30794
- const styles$r = StyleSheet.create({
31035
+ const styles$s = StyleSheet.create({
30795
31036
  row: {
30796
31037
  display: "flex",
30797
31038
  flexDirection: "row",
@@ -30832,7 +31073,7 @@ const LockedPointSettings = props => {
30832
31073
  /*#__PURE__*/
30833
31074
  // Summary: Point, coords, color (filled/open)
30834
31075
  React.createElement(View, {
30835
- style: styles$q.row
31076
+ style: styles$r.row
30836
31077
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Point (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React.createElement(Strut, {
30837
31078
  size: spacing.xSmall_8
30838
31079
  }), /*#__PURE__*/React.createElement(ColorSwatch, {
@@ -30841,7 +31082,7 @@ const LockedPointSettings = props => {
30841
31082
  }))
30842
31083
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30843
31084
  coord: coord,
30844
- style: styles$q.spaceUnder,
31085
+ style: styles$r.spaceUnder,
30845
31086
  onChange: newCoord => {
30846
31087
  onChangeProps({
30847
31088
  coord: newCoord
@@ -30850,7 +31091,7 @@ const LockedPointSettings = props => {
30850
31091
  }), /*#__PURE__*/React.createElement(ColorSelect, {
30851
31092
  selectedValue: pointColor,
30852
31093
  onChange: handleColorChange,
30853
- style: styles$q.spaceUnder
31094
+ style: styles$r.spaceUnder
30854
31095
  }), /*#__PURE__*/React.createElement(LabeledSwitch, {
30855
31096
  label: "open point",
30856
31097
  checked: !filled,
@@ -30865,7 +31106,7 @@ const LockedPointSettings = props => {
30865
31106
  onRemove: onRemove
30866
31107
  }));
30867
31108
  };
30868
- const styles$q = StyleSheet.create({
31109
+ const styles$r = StyleSheet.create({
30869
31110
  row: {
30870
31111
  flexDirection: "row",
30871
31112
  alignItems: "center"
@@ -30883,17 +31124,17 @@ const PolygonSwatch = props => {
30883
31124
  } = props;
30884
31125
  return /*#__PURE__*/React.createElement(View, {
30885
31126
  "aria-label": `${color}, stroke ${strokeStyle}, fill ${fillStyle}`,
30886
- style: [styles$p.container, {
31127
+ style: [styles$q.container, {
30887
31128
  border: `4px ${strokeStyle} ${lockedFigureColors[color]}`
30888
31129
  }]
30889
31130
  }, /*#__PURE__*/React.createElement(View, {
30890
- style: [styles$p.innerSquare, {
31131
+ style: [styles$q.innerSquare, {
30891
31132
  backgroundColor: lockedFigureColors[color],
30892
31133
  opacity: fillStyle === "white" ? 0 : lockedFigureFillStyles[fillStyle]
30893
31134
  }]
30894
31135
  }));
30895
31136
  };
30896
- const styles$p = StyleSheet.create({
31137
+ const styles$q = StyleSheet.create({
30897
31138
  container: {
30898
31139
  // Add a white outline so that the color swatch is visible when
30899
31140
  // the dropdown option is highlighted with its blue background.
@@ -30959,7 +31200,7 @@ const LockedPolygonSettings = props => {
30959
31200
  /*#__PURE__*/
30960
31201
  // Summary: Polygon, number of sides, style swatch
30961
31202
  React.createElement(View, {
30962
- style: styles$o.row
31203
+ style: styles$p.row
30963
31204
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Polygon, ${points.length} sides`), /*#__PURE__*/React.createElement(Strut, {
30964
31205
  size: spacing.xSmall_8
30965
31206
  }), /*#__PURE__*/React.createElement(PolygonSwatch, {
@@ -30968,7 +31209,7 @@ const LockedPolygonSettings = props => {
30968
31209
  strokeStyle: strokeStyle
30969
31210
  }))
30970
31211
  }, /*#__PURE__*/React.createElement(View, {
30971
- style: [styles$o.row, styles$o.spaceUnder]
31212
+ style: [styles$p.row, styles$p.spaceUnder]
30972
31213
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30973
31214
  selectedValue: color,
30974
31215
  onChange: handleColorChange
@@ -30976,7 +31217,7 @@ const LockedPolygonSettings = props => {
30976
31217
  size: spacing.medium_16
30977
31218
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30978
31219
  tag: "label",
30979
- style: [styles$o.row, styles$o.truncatedWidth]
31220
+ style: [styles$p.row, styles$p.truncatedWidth]
30980
31221
  }, "fill", /*#__PURE__*/React.createElement(Strut, {
30981
31222
  size: spacing.xxSmall_6
30982
31223
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -31002,17 +31243,17 @@ const LockedPolygonSettings = props => {
31002
31243
  onChange: newValue => onChangeProps({
31003
31244
  showVertices: newValue
31004
31245
  }),
31005
- style: styles$o.spaceUnder
31246
+ style: styles$p.spaceUnder
31006
31247
  }), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31007
31248
  header: /*#__PURE__*/React.createElement(LabelLarge, null, "Points"),
31008
31249
  expanded: true,
31009
- containerStyle: styles$o.pointAccordionContainer,
31010
- panelStyle: styles$o.pointAccordionPanel
31250
+ containerStyle: styles$p.pointAccordionContainer,
31251
+ panelStyle: styles$p.pointAccordionPanel
31011
31252
  }, points.map((point, index) => {
31012
31253
  const pointLabel = String.fromCharCode(65 + index);
31013
31254
  return /*#__PURE__*/React.createElement(View, {
31014
31255
  key: `locked-polygon-point-index-${index}`,
31015
- style: [styles$o.row, styles$o.spaceUnder]
31256
+ style: [styles$p.row, styles$p.spaceUnder]
31016
31257
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `${pointLabel}:`), /*#__PURE__*/React.createElement(Strut, {
31017
31258
  size: spacing.medium_16
31018
31259
  }), /*#__PURE__*/React.createElement(CoordinatePairInput, {
@@ -31040,10 +31281,10 @@ const LockedPolygonSettings = props => {
31040
31281
  points: newPoints
31041
31282
  });
31042
31283
  },
31043
- style: styles$o.icon
31284
+ style: styles$p.icon
31044
31285
  }));
31045
31286
  }), /*#__PURE__*/React.createElement(View, {
31046
- style: [styles$o.row, styles$o.polygonActionsContainer]
31287
+ style: [styles$p.row, styles$p.polygonActionsContainer]
31047
31288
  }, /*#__PURE__*/React.createElement(Button, {
31048
31289
  kind: "tertiary",
31049
31290
  startIcon: plusCircle,
@@ -31053,30 +31294,30 @@ const LockedPolygonSettings = props => {
31053
31294
  });
31054
31295
  }
31055
31296
  }, "Add point"), /*#__PURE__*/React.createElement(Spring, null), /*#__PURE__*/React.createElement(View, {
31056
- style: styles$o.movementButtonsContainer
31297
+ style: styles$p.movementButtonsContainer
31057
31298
  }, /*#__PURE__*/React.createElement(IconButton, {
31058
31299
  "aria-label": "Move polygon up",
31059
- style: styles$o.iconButton,
31300
+ style: styles$p.iconButton,
31060
31301
  size: "small",
31061
31302
  icon: arrowFatUp,
31062
31303
  onClick: () => handlePolygonMove("up")
31063
31304
  }), /*#__PURE__*/React.createElement(View, {
31064
- style: styles$o.row
31305
+ style: styles$p.row
31065
31306
  }, /*#__PURE__*/React.createElement(IconButton, {
31066
31307
  "aria-label": "Move polygon left",
31067
- style: styles$o.iconButton,
31308
+ style: styles$p.iconButton,
31068
31309
  size: "small",
31069
31310
  icon: arrowFatLeft,
31070
31311
  onClick: () => handlePolygonMove("left")
31071
31312
  }), /*#__PURE__*/React.createElement(IconButton, {
31072
31313
  "aria-label": "Move polygon down",
31073
- style: styles$o.iconButton,
31314
+ style: styles$p.iconButton,
31074
31315
  size: "small",
31075
31316
  icon: arrowFatDown,
31076
31317
  onClick: () => handlePolygonMove("down")
31077
31318
  }), /*#__PURE__*/React.createElement(IconButton, {
31078
31319
  "aria-label": "Move polygon right",
31079
- style: styles$o.iconButton,
31320
+ style: styles$p.iconButton,
31080
31321
  size: "small",
31081
31322
  icon: arrowFatRight,
31082
31323
  onClick: () => handlePolygonMove("right")
@@ -31086,7 +31327,7 @@ const LockedPolygonSettings = props => {
31086
31327
  onRemove: onRemove
31087
31328
  }));
31088
31329
  };
31089
- const styles$o = StyleSheet.create({
31330
+ const styles$p = StyleSheet.create({
31090
31331
  row: {
31091
31332
  display: "flex",
31092
31333
  flexDirection: "row",
@@ -31160,7 +31401,7 @@ const LockedVectorSettings = props => {
31160
31401
  expanded: props.expanded,
31161
31402
  onToggle: props.onToggle,
31162
31403
  header: /*#__PURE__*/React.createElement(View, {
31163
- style: styles$n.row
31404
+ style: styles$o.row
31164
31405
  }, /*#__PURE__*/React.createElement(LabelLarge, null, lineLabel), /*#__PURE__*/React.createElement(Strut, {
31165
31406
  size: spacing.xSmall_8
31166
31407
  }), /*#__PURE__*/React.createElement(LineSwatch, {
@@ -31168,19 +31409,19 @@ const LockedVectorSettings = props => {
31168
31409
  lineStyle: "solid"
31169
31410
  }))
31170
31411
  }, /*#__PURE__*/React.createElement(View, {
31171
- style: [styles$n.row, styles$n.spaceUnder]
31412
+ style: [styles$o.row, styles$o.spaceUnder]
31172
31413
  }, /*#__PURE__*/React.createElement(ColorSelect, {
31173
31414
  selectedValue: lineColor,
31174
31415
  onChange: handleColorChange
31175
31416
  })), isInvalid && /*#__PURE__*/React.createElement(LabelMedium, {
31176
- style: styles$n.errorText
31417
+ style: styles$o.errorText
31177
31418
  }, lengthErrorMessage), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31178
31419
  expanded: true // Initial state is expanded
31179
31420
  ,
31180
- containerStyle: styles$n.container,
31181
- panelStyle: styles$n.accordionPanel,
31421
+ containerStyle: styles$o.container,
31422
+ panelStyle: styles$o.accordionPanel,
31182
31423
  header: /*#__PURE__*/React.createElement(View, {
31183
- style: styles$n.row
31424
+ style: styles$o.row
31184
31425
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Tail (${tail[0]}, ${tail[1]})`))
31185
31426
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31186
31427
  coord: tail,
@@ -31191,10 +31432,10 @@ const LockedVectorSettings = props => {
31191
31432
  })), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31192
31433
  expanded: true // Initial state is expanded
31193
31434
  ,
31194
- containerStyle: styles$n.container,
31195
- panelStyle: styles$n.accordionPanel,
31435
+ containerStyle: styles$o.container,
31436
+ panelStyle: styles$o.accordionPanel,
31196
31437
  header: /*#__PURE__*/React.createElement(View, {
31197
- style: styles$n.row
31438
+ style: styles$o.row
31198
31439
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Tip (${tip[0]}, ${tip[1]})`))
31199
31440
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31200
31441
  coord: tip,
@@ -31208,7 +31449,7 @@ const LockedVectorSettings = props => {
31208
31449
  onRemove: onRemove
31209
31450
  }));
31210
31451
  };
31211
- const styles$n = StyleSheet.create({
31452
+ const styles$o = StyleSheet.create({
31212
31453
  accordionPanel: {
31213
31454
  paddingBottom: spacing.medium_16
31214
31455
  },
@@ -31251,10 +31492,7 @@ const LockedFigureSettings = props => {
31251
31492
  case "polygon":
31252
31493
  return /*#__PURE__*/React.createElement(LockedPolygonSettings, props);
31253
31494
  case "function":
31254
- if (props.showM2bFeatures) {
31255
- return /*#__PURE__*/React.createElement(LockedFunctionSettings, props);
31256
- }
31257
- break;
31495
+ return /*#__PURE__*/React.createElement(LockedFunctionSettings, props);
31258
31496
  case "label":
31259
31497
  if (props.showLabelsFlag) {
31260
31498
  return /*#__PURE__*/React.createElement(LockedLabelSettings, props);
@@ -31365,7 +31603,6 @@ const LockedFiguresSection = props => {
31365
31603
  }), isExpanded && /*#__PURE__*/React.createElement(View, null, figures == null ? void 0 : figures.map((figure, index) => {
31366
31604
  return /*#__PURE__*/React.createElement(LockedFigureSettings, _extends({
31367
31605
  key: `${uniqueId}-locked-${figure}-${index}`,
31368
- showM2bFeatures: props.showM2bFeatures,
31369
31606
  showLabelsFlag: props.showLabelsFlag,
31370
31607
  expanded: expandedStates[index],
31371
31608
  onToggle: newValue => {
@@ -31379,9 +31616,8 @@ const LockedFiguresSection = props => {
31379
31616
  onRemove: () => removeLockedFigure(index)
31380
31617
  }));
31381
31618
  }), /*#__PURE__*/React.createElement(View, {
31382
- style: styles$m.buttonContainer
31619
+ style: styles$n.buttonContainer
31383
31620
  }, /*#__PURE__*/React.createElement(LockedFigureSelect, {
31384
- showM2bFeatures: props.showM2bFeatures,
31385
31621
  showLabelsFlag: props.showLabelsFlag,
31386
31622
  id: `${uniqueId}-select`,
31387
31623
  onChange: addLockedFigure
@@ -31390,10 +31626,10 @@ const LockedFiguresSection = props => {
31390
31626
  }), showExpandButton && /*#__PURE__*/React.createElement(Button, {
31391
31627
  kind: "secondary",
31392
31628
  onClick: () => toggleExpanded(allCollapsed),
31393
- style: styles$m.button
31629
+ style: styles$n.button
31394
31630
  }, buttonLabel))));
31395
31631
  };
31396
- const styles$m = StyleSheet.create({
31632
+ const styles$n = StyleSheet.create({
31397
31633
  buttonContainer: {
31398
31634
  flexDirection: "row",
31399
31635
  alignItems: "center"
@@ -31428,7 +31664,7 @@ const GraphPointsCountSelector = ({
31428
31664
  // Never uses placeholder, always has value
31429
31665
  ,
31430
31666
  placeholder: "",
31431
- style: styles$l.singleSelectShort
31667
+ style: styles$m.singleSelectShort
31432
31668
  }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React.createElement(OptionItem, {
31433
31669
  key: n,
31434
31670
  value: `${n}`,
@@ -31439,7 +31675,7 @@ const GraphPointsCountSelector = ({
31439
31675
  label: "unlimited"
31440
31676
  })]);
31441
31677
  };
31442
- const styles$l = StyleSheet.create({
31678
+ const styles$m = StyleSheet.create({
31443
31679
  singleSelectShort: {
31444
31680
  // Non-standard spacing, but it's the smallest we can go
31445
31681
  // without running into styling issues with the dropdown.
@@ -31452,7 +31688,7 @@ const GraphTypeSelector = props => {
31452
31688
  selectedValue: props.graphType,
31453
31689
  onChange: props.onChange,
31454
31690
  placeholder: "Select a graph type",
31455
- style: styles$k.singleSelectShort
31691
+ style: styles$l.singleSelectShort
31456
31692
  }, /*#__PURE__*/React.createElement(OptionItem, {
31457
31693
  value: "linear",
31458
31694
  label: "Linear function"
@@ -31485,7 +31721,7 @@ const GraphTypeSelector = props => {
31485
31721
  label: "Angle"
31486
31722
  }));
31487
31723
  };
31488
- const styles$k = StyleSheet.create({
31724
+ const styles$l = StyleSheet.create({
31489
31725
  singleSelectShort: {
31490
31726
  // Non-standard spacing, but it's the smallest we can go
31491
31727
  // without running into styling issues with the dropdown.
@@ -31516,6 +31752,53 @@ function InteractiveGraphCorrectAnswer(props) {
31516
31752
  }, props.equationString)), props.children));
31517
31753
  }
31518
31754
 
31755
+ function InteractiveGraphDescription(props) {
31756
+ const {
31757
+ ariaLabelValue,
31758
+ ariaDescriptionValue,
31759
+ onChange
31760
+ } = props;
31761
+ const [isOpen, setIsOpen] = React.useState(true);
31762
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
31763
+ title: "Description",
31764
+ isCollapsible: true,
31765
+ isOpen: isOpen,
31766
+ onToggle: setIsOpen
31767
+ }), isOpen && /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(LabelXSmall, {
31768
+ style: styles$k.caption
31769
+ }, "Use these fields to describe the graph as a whole. These are used by screen readers to describe content to users who are visually impaired."), /*#__PURE__*/React.createElement(LabelLarge, {
31770
+ tag: "label"
31771
+ }, "Title", /*#__PURE__*/React.createElement(TextField, {
31772
+ value: ariaLabelValue,
31773
+ onChange: newValue => onChange({
31774
+ fullGraphAriaLabel: newValue
31775
+ }),
31776
+ style: styles$k.spaceAbove
31777
+ })), /*#__PURE__*/React.createElement(Strut, {
31778
+ size: spacing.small_12
31779
+ }), /*#__PURE__*/React.createElement(LabelLarge, {
31780
+ tag: "label"
31781
+ }, "Description", /*#__PURE__*/React.createElement(TextArea, {
31782
+ rows: 8,
31783
+ resizeType: "vertical",
31784
+ value: ariaDescriptionValue,
31785
+ onChange: newValue => onChange({
31786
+ fullGraphAriaDescription: newValue
31787
+ }),
31788
+ style: styles$k.spaceAbove
31789
+ }))));
31790
+ }
31791
+ const styles$k = StyleSheet.create({
31792
+ caption: {
31793
+ color: color.offBlack64,
31794
+ paddingTop: spacing.xxSmall_6,
31795
+ paddingBottom: spacing.xxSmall_6
31796
+ },
31797
+ spaceAbove: {
31798
+ marginTop: spacing.xxxSmall_4
31799
+ }
31800
+ });
31801
+
31519
31802
  const {
31520
31803
  ButtonGroup: ButtonGroup$2,
31521
31804
  InfoTip: InfoTip$c,
@@ -32465,14 +32748,6 @@ class InteractiveGraphEditor extends React.Component {
32465
32748
  super(...args);
32466
32749
  this.displayName = "InteractiveGraphEditor";
32467
32750
  this.className = "perseus-widget-interactive-graph";
32468
- this.changeMatchType = newValue => {
32469
- const correct = _extends({}, this.props.correct, {
32470
- match: newValue
32471
- });
32472
- this.props.onChange({
32473
- correct: correct
32474
- });
32475
- };
32476
32751
  this.changeStartCoords = coords => {
32477
32752
  var _this$props$graph;
32478
32753
  if (!((_this$props$graph = this.props.graph) != null && _this$props$graph.type)) {
@@ -32503,7 +32778,7 @@ class InteractiveGraphEditor extends React.Component {
32503
32778
  // is copied from the question editor and pasted into the hint editor
32504
32779
  // (double brackets in the markdown).
32505
32780
  serialize() {
32506
- const json = _.pick(this.props, "step", "backgroundImage", "markings", "labels", "showProtractor", "showTooltips", "range", "gridStep", "snapStep", "lockedFigures");
32781
+ const json = _.pick(this.props, "step", "backgroundImage", "markings", "labels", "showProtractor", "showTooltips", "range", "gridStep", "snapStep", "lockedFigures", "fullGraphAriaLabel", "fullGraphAriaDescription");
32507
32782
 
32508
32783
  // eslint-disable-next-line react/no-string-refs
32509
32784
  const graph = this.refs.graph;
@@ -32529,7 +32804,7 @@ class InteractiveGraphEditor extends React.Component {
32529
32804
  return json;
32530
32805
  }
32531
32806
  render() {
32532
- var _this$props$graph$typ, _this$props$graph3, _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$graph6, _this$props$apiOption, _this$props$apiOption2, _this$props$apiOption3;
32807
+ var _this$props$graph$typ, _this$props$graph3, _this$props$apiOption, _this$props$fullGraph, _this$props$fullGraph2, _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$graph9, _this$props$apiOption2, _this$props$apiOption3;
32533
32808
  let graph;
32534
32809
  let equationString;
32535
32810
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, interactiveSizes.defaultBoxSize);
@@ -32553,15 +32828,21 @@ class InteractiveGraphEditor extends React.Component {
32553
32828
  showProtractor: this.props.showProtractor,
32554
32829
  showTooltips: this.props.showTooltips,
32555
32830
  lockedFigures: this.props.lockedFigures,
32831
+ fullGraphAriaLabel: this.props.fullGraphAriaLabel,
32832
+ fullGraphAriaDescription: this.props.fullGraphAriaDescription,
32556
32833
  trackInteraction: function () {},
32557
- onChange: newProps => {
32834
+ onChange: ({
32835
+ graph: newGraph
32836
+ }) => {
32558
32837
  let correct = this.props.correct;
32559
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
32560
- if (correct.type === newProps.graph.type) {
32561
- correct = _extends({}, correct, newProps.graph);
32838
+ // TODO(benchristel): can we improve the type of onChange
32839
+ // so this invariant isn't necessary?
32840
+ invariant(newGraph != null);
32841
+ if (correct.type === newGraph.type) {
32842
+ correct = mergeGraphs(correct, newGraph);
32562
32843
  } else {
32563
32844
  // Clear options from previous graph
32564
- correct = newProps.graph;
32845
+ correct = newGraph;
32565
32846
  }
32566
32847
  this.props.onChange({
32567
32848
  correct: correct,
@@ -32604,7 +32885,11 @@ class InteractiveGraphEditor extends React.Component {
32604
32885
  }
32605
32886
  });
32606
32887
  }
32607
- })), /*#__PURE__*/React.createElement(InteractiveGraphCorrectAnswer, {
32888
+ })), 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(InteractiveGraphDescription, {
32889
+ ariaLabelValue: (_this$props$fullGraph = this.props.fullGraphAriaLabel) != null ? _this$props$fullGraph : "",
32890
+ ariaDescriptionValue: (_this$props$fullGraph2 = this.props.fullGraphAriaDescription) != null ? _this$props$fullGraph2 : "",
32891
+ onChange: this.props.onChange
32892
+ }), /*#__PURE__*/React.createElement(InteractiveGraphCorrectAnswer, {
32608
32893
  equationString: equationString
32609
32894
  }, graph), ((_this$props$correct = this.props.correct) == null ? void 0 : _this$props$correct.type) === "point" && /*#__PURE__*/React.createElement(LabeledRow, {
32610
32895
  label: "Number of Points:"
@@ -32629,17 +32914,19 @@ class InteractiveGraphEditor extends React.Component {
32629
32914
  selectedValue: (_this$props$correct4 = this.props.correct) != null && _this$props$correct4.numSides ? `${this.props.correct.numSides}` : "3",
32630
32915
  placeholder: "",
32631
32916
  onChange: newValue => {
32632
- const graph = _extends({}, this.props.correct, {
32917
+ var _this$props$graph4;
32918
+ invariant(((_this$props$graph4 = this.props.graph) == null ? void 0 : _this$props$graph4.type) === "polygon");
32919
+ const updates = {
32633
32920
  numSides: parsePointCount(newValue),
32634
32921
  coords: null,
32635
32922
  // reset the snap for UNLIMITED, which
32636
32923
  // only supports "grid"
32637
32924
  // From: D6578
32638
32925
  snapTo: "grid"
32639
- });
32926
+ };
32640
32927
  this.props.onChange({
32641
- correct: graph,
32642
- graph: graph
32928
+ correct: _extends({}, this.props.correct, updates),
32929
+ graph: _extends({}, this.props.graph, updates)
32643
32930
  });
32644
32931
  },
32645
32932
  style: styles$a.singleSelectShort
@@ -32655,13 +32942,16 @@ class InteractiveGraphEditor extends React.Component {
32655
32942
  ,
32656
32943
  placeholder: "",
32657
32944
  onChange: newValue => {
32658
- const graph = _extends({}, this.props.correct, {
32945
+ var _this$props$graph5, _this$props$graph6;
32946
+ invariant(this.props.correct.type === "polygon", `Expected correct answer type to be polygon, but got ${this.props.correct.type}`);
32947
+ invariant(((_this$props$graph5 = this.props.graph) == null ? void 0 : _this$props$graph5.type) === "polygon", `Expected graph type to be polygon, but got ${(_this$props$graph6 = this.props.graph) == null ? void 0 : _this$props$graph6.type}`);
32948
+ const updates = {
32659
32949
  snapTo: newValue,
32660
32950
  coords: null
32661
- });
32951
+ };
32662
32952
  this.props.onChange({
32663
- correct: graph,
32664
- graph: graph
32953
+ correct: _extends({}, this.props.correct, updates),
32954
+ graph: _extends({}, this.props.graph, updates)
32665
32955
  });
32666
32956
  },
32667
32957
  style: styles$a.singleSelectShort
@@ -32682,8 +32972,9 @@ class InteractiveGraphEditor extends React.Component {
32682
32972
  // Don't show indeterminate checkbox state
32683
32973
  !!((_this$props$correct8 = this.props.correct) != null && _this$props$correct8.showAngles),
32684
32974
  onChange: () => {
32685
- var _this$props$graph4;
32686
- if (((_this$props$graph4 = this.props.graph) == null ? void 0 : _this$props$graph4.type) === "polygon") {
32975
+ var _this$props$graph7;
32976
+ if (((_this$props$graph7 = this.props.graph) == null ? void 0 : _this$props$graph7.type) === "polygon") {
32977
+ invariant(this.props.correct.type === "polygon", `Expected graph type to be polygon, but got ${this.props.correct.type}`);
32687
32978
  this.props.onChange({
32688
32979
  correct: _extends({}, this.props.correct, {
32689
32980
  showAngles: !this.props.correct.showAngles
@@ -32702,8 +32993,8 @@ class InteractiveGraphEditor extends React.Component {
32702
32993
  // Don't show indeterminate checkbox state
32703
32994
  !!((_this$props$correct9 = this.props.correct) != null && _this$props$correct9.showSides),
32704
32995
  onChange: () => {
32705
- var _this$props$graph5;
32706
- if (((_this$props$graph5 = this.props.graph) == null ? void 0 : _this$props$graph5.type) === "polygon") {
32996
+ var _this$props$graph8;
32997
+ if (((_this$props$graph8 = this.props.graph) == null ? void 0 : _this$props$graph8.type) === "polygon" && this.props.correct.type === "polygon") {
32707
32998
  this.props.onChange({
32708
32999
  correct: _extends({}, this.props.correct, {
32709
33000
  showSides: !this.props.correct.showSides
@@ -32731,9 +33022,7 @@ class InteractiveGraphEditor extends React.Component {
32731
33022
  }
32732
33023
  });
32733
33024
  }
32734
- })), ((_this$props$graph6 = this.props.graph) == null ? void 0 : _this$props$graph6.type) &&
32735
- // TODO(LEMS-2228): Remove flags once this is fully released
32736
- shouldShowStartCoordsUI(this.props.apiOptions.flags, this.props.graph) && /*#__PURE__*/React.createElement(StartCoordsSettings, _extends({}, this.props.graph, {
33025
+ })), ((_this$props$graph9 = this.props.graph) == null ? void 0 : _this$props$graph9.type) && shouldShowStartCoordsUI(this.props.graph, this.props.static) && /*#__PURE__*/React.createElement(StartCoordsSettings, _extends({}, this.props.graph, {
32737
33026
  range: this.props.range,
32738
33027
  step: this.props.step,
32739
33028
  onChange: this.changeStartCoords
@@ -32754,7 +33043,22 @@ class InteractiveGraphEditor extends React.Component {
32754
33043
  label: "Student answer must"
32755
33044
  }, /*#__PURE__*/React.createElement(SingleSelect, {
32756
33045
  selectedValue: this.props.correct.match || "exact",
32757
- onChange: this.changeMatchType
33046
+ onChange: newValue => {
33047
+ invariant(this.props.correct.type === "polygon", `Expected graph type to be polygon, but got ${this.props.correct.type}`);
33048
+ const correct = _extends({}, this.props.correct, {
33049
+ // TODO(benchristel): this cast is necessary
33050
+ // because "exact" is not actually a valid
33051
+ // value for `match`; a value of undefined
33052
+ // means exact matching. The code happens
33053
+ // to work because "exact" falls through
33054
+ // to the correct else branch in
33055
+ // InteractiveGraph.validate()
33056
+ match: newValue
33057
+ });
33058
+ this.props.onChange({
33059
+ correct
33060
+ });
33061
+ }
32758
33062
  // Never uses placeholder, always has value
32759
33063
  ,
32760
33064
  placeholder: "",
@@ -32775,7 +33079,20 @@ class InteractiveGraphEditor extends React.Component {
32775
33079
  label: "Student answer must"
32776
33080
  }, /*#__PURE__*/React.createElement(SingleSelect, {
32777
33081
  selectedValue: this.props.correct.match || "exact",
32778
- onChange: this.changeMatchType
33082
+ onChange: newValue => {
33083
+ this.props.onChange({
33084
+ correct: _extends({}, this.props.correct, {
33085
+ // TODO(benchristel): this cast is necessary
33086
+ // because "exact" is not actually a valid
33087
+ // value for `match`; a value of undefined
33088
+ // means exact matching. The code happens
33089
+ // to work because "exact" falls through
33090
+ // to the correct else branch in
33091
+ // InteractiveGraph.validate()
33092
+ match: newValue
33093
+ })
33094
+ });
33095
+ }
32779
33096
  // Never uses placeholder, always has value
32780
33097
  ,
32781
33098
  placeholder: "",
@@ -32789,14 +33106,17 @@ class InteractiveGraphEditor extends React.Component {
32789
33106
  })), /*#__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."))),
32790
33107
  // Only show the "Add locked figure" dropdown if the graph
32791
33108
  // is using Mafs.
32792
- 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, {
32793
- showM2bFeatures: (_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["interactive-graph-locked-features-m2b"],
33109
+ this.props.graph && ((_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, {
32794
33110
  showLabelsFlag: (_this$props$apiOption3 = this.props.apiOptions) == null || (_this$props$apiOption3 = _this$props$apiOption3.flags) == null || (_this$props$apiOption3 = _this$props$apiOption3.mafs) == null ? void 0 : _this$props$apiOption3["interactive-graph-locked-features-labels"],
32795
33111
  figures: this.props.lockedFigures,
32796
33112
  onChange: this.props.onChange
32797
33113
  }));
32798
33114
  }
32799
33115
  }
33116
+
33117
+ // Merges two graphs that have the same `type`. Properties defined in `b`
33118
+ // overwrite properties of the same name in `a`. Throws an exception if the
33119
+ // types are different or not recognized.
32800
33120
  InteractiveGraphEditor.widgetName = "interactive-graph";
32801
33121
  InteractiveGraphEditor.defaultProps = _extends({}, InteractiveGraph.defaultProps, {
32802
33122
  valid: true,
@@ -32807,6 +33127,45 @@ InteractiveGraphEditor.defaultProps = _extends({}, InteractiveGraph.defaultProps
32807
33127
  coords: null
32808
33128
  }
32809
33129
  });
33130
+ function mergeGraphs(a, b) {
33131
+ if (a.type !== b.type) {
33132
+ throw new Error(`Cannot merge graphs with different types (${a.type} and ${b.type})`);
33133
+ }
33134
+ switch (a.type) {
33135
+ case "angle":
33136
+ invariant(b.type === "angle");
33137
+ return _extends({}, a, b);
33138
+ case "circle":
33139
+ invariant(b.type === "circle");
33140
+ return _extends({}, a, b);
33141
+ case "linear":
33142
+ invariant(b.type === "linear");
33143
+ return _extends({}, a, b);
33144
+ case "linear-system":
33145
+ invariant(b.type === "linear-system");
33146
+ return _extends({}, a, b);
33147
+ case "point":
33148
+ invariant(b.type === "point");
33149
+ return _extends({}, a, b);
33150
+ case "polygon":
33151
+ invariant(b.type === "polygon");
33152
+ return _extends({}, a, b);
33153
+ case "quadratic":
33154
+ invariant(b.type === "quadratic");
33155
+ return _extends({}, a, b);
33156
+ case "ray":
33157
+ invariant(b.type === "ray");
33158
+ return _extends({}, a, b);
33159
+ case "segment":
33160
+ invariant(b.type === "segment");
33161
+ return _extends({}, a, b);
33162
+ case "sinusoid":
33163
+ invariant(b.type === "sinusoid");
33164
+ return _extends({}, a, b);
33165
+ default:
33166
+ throw new UnreachableCaseError(a);
33167
+ }
33168
+ }
32810
33169
  const styles$a = StyleSheet.create({
32811
33170
  singleSelectShort: {
32812
33171
  // Non-standard spacing, but it's the smallest we can go