@khanacademy/perseus-editor 14.0.0 → 14.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -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';
@@ -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.0";
47
+ const libVersion = "14.1.0";
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
@@ -29681,17 +29831,17 @@ const LockedFigureSelect = props => {
29681
29831
  figureTypes.push("label");
29682
29832
  }
29683
29833
  return /*#__PURE__*/React.createElement(View, {
29684
- style: styles$G.container
29834
+ style: styles$H.container
29685
29835
  }, /*#__PURE__*/React.createElement(ActionMenu, {
29686
29836
  menuText: "Add locked figure",
29687
- style: styles$G.addElementSelect
29837
+ style: styles$H.addElementSelect
29688
29838
  }, figureTypes.map(figureType => /*#__PURE__*/React.createElement(ActionItem, {
29689
29839
  key: `${id}-${figureType}`,
29690
29840
  label: figureType,
29691
29841
  onClick: () => onChange(figureType)
29692
29842
  }))));
29693
29843
  };
29694
- const styles$G = StyleSheet.create({
29844
+ const styles$H = StyleSheet.create({
29695
29845
  container: {
29696
29846
  marginTop: spacing.xSmall_8
29697
29847
  },
@@ -29761,18 +29911,18 @@ const AngleInput = props => {
29761
29911
  }
29762
29912
  return /*#__PURE__*/React.createElement(LabelMedium, {
29763
29913
  tag: "label",
29764
- style: styles$F.row
29914
+ style: styles$G.row
29765
29915
  }, "angle (degrees)", /*#__PURE__*/React.createElement(Strut, {
29766
29916
  size: spacing.xxSmall_6
29767
29917
  }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29768
29918
  value: angleInput,
29769
29919
  onChange: handleAngleChange,
29770
- style: styles$F.textField
29920
+ style: styles$G.textField
29771
29921
  }), /*#__PURE__*/React.createElement(Strut, {
29772
29922
  size: spacing.xxSmall_6
29773
29923
  }));
29774
29924
  };
29775
- const styles$F = StyleSheet.create({
29925
+ const styles$G = StyleSheet.create({
29776
29926
  row: {
29777
29927
  display: "flex",
29778
29928
  flexDirection: "row",
@@ -29821,30 +29971,30 @@ const CoordinatePairInput = props => {
29821
29971
  onChange(newCoords);
29822
29972
  }
29823
29973
  return /*#__PURE__*/React.createElement(View, {
29824
- style: [styles$E.row, style]
29974
+ style: [styles$F.row, style]
29825
29975
  }, /*#__PURE__*/React.createElement(LabelMedium, {
29826
29976
  tag: "label",
29827
- style: styles$E.row
29977
+ style: styles$F.row
29828
29978
  }, labels ? labels[0] : "x coord", /*#__PURE__*/React.createElement(Strut, {
29829
29979
  size: spacing.xxSmall_6
29830
29980
  }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29831
29981
  value: coordState[0],
29832
29982
  onChange: newValue => handleCoordChange(newValue, 0),
29833
- style: [styles$E.textField, error ? styles$E.errorField : undefined]
29983
+ style: [styles$F.textField, error ? styles$F.errorField : undefined]
29834
29984
  })), /*#__PURE__*/React.createElement(Strut, {
29835
29985
  size: spacing.medium_16
29836
29986
  }), /*#__PURE__*/React.createElement(LabelMedium, {
29837
29987
  tag: "label",
29838
- style: styles$E.row
29988
+ style: styles$F.row
29839
29989
  }, labels ? labels[1] : "y coord", /*#__PURE__*/React.createElement(Strut, {
29840
29990
  size: spacing.xxSmall_6
29841
29991
  }), /*#__PURE__*/React.createElement(ScrolllessNumberTextField, {
29842
29992
  value: coordState[1],
29843
29993
  onChange: newValue => handleCoordChange(newValue, 1),
29844
- style: [styles$E.textField, error ? styles$E.errorField : undefined]
29994
+ style: [styles$F.textField, error ? styles$F.errorField : undefined]
29845
29995
  })));
29846
29996
  };
29847
- const styles$E = StyleSheet.create({
29997
+ const styles$F = StyleSheet.create({
29848
29998
  row: {
29849
29999
  display: "flex",
29850
30000
  flexDirection: "row",
@@ -29877,14 +30027,14 @@ const PerseusEditorAccordion = props => {
29877
30027
  }, /*#__PURE__*/React.createElement(AccordionSection, {
29878
30028
  expanded: expanded,
29879
30029
  onToggle: onToggle,
29880
- style: [styles$D.container, containerStyle],
29881
- headerStyle: styles$D.accordionHeader,
30030
+ style: [styles$E.container, containerStyle],
30031
+ headerStyle: styles$E.accordionHeader,
29882
30032
  header: header
29883
30033
  }, /*#__PURE__*/React.createElement(View, {
29884
- style: [styles$D.accordionPanel, panelStyle]
30034
+ style: [styles$E.accordionPanel, panelStyle]
29885
30035
  }, children)));
29886
30036
  };
29887
- const styles$D = StyleSheet.create({
30037
+ const styles$E = StyleSheet.create({
29888
30038
  container: {
29889
30039
  backgroundColor: color.fadedBlue8,
29890
30040
  marginTop: spacing.xSmall_8
@@ -29915,13 +30065,13 @@ const ColorSwatch = props => {
29915
30065
  "aria-label":
29916
30066
  // Don't include an aria label for decorative circles.
29917
30067
  !decorative ? `${color$1}, ${filled ? "filled" : "open"}` : undefined,
29918
- style: [styles$C.colorSwatch, {
30068
+ style: [styles$D.colorSwatch, {
29919
30069
  border: `4px solid ${lockedFigureColors[color$1]}`,
29920
30070
  backgroundColor: filled ? lockedFigureColors[color$1] : color.white
29921
30071
  }]
29922
30072
  });
29923
30073
  };
29924
- const styles$C = StyleSheet.create({
30074
+ const styles$D = StyleSheet.create({
29925
30075
  colorSwatch: {
29926
30076
  // Add a white outline so that the color swatch is visible when
29927
30077
  // the dropdown option is highlighted with its blue background.
@@ -29940,10 +30090,10 @@ const ColorSelect = props => {
29940
30090
  onChange
29941
30091
  } = props;
29942
30092
  return /*#__PURE__*/React.createElement(View, {
29943
- style: [styles$B.row, style]
30093
+ style: [styles$C.row, style]
29944
30094
  }, /*#__PURE__*/React.createElement(LabelMedium, {
29945
30095
  tag: "label",
29946
- style: styles$B.row
30096
+ style: styles$C.row
29947
30097
  }, "color", /*#__PURE__*/React.createElement(Strut, {
29948
30098
  size: spacing.xxSmall_6
29949
30099
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -29962,7 +30112,7 @@ const ColorSelect = props => {
29962
30112
  })
29963
30113
  })))));
29964
30114
  };
29965
- const styles$B = StyleSheet.create({
30115
+ const styles$C = StyleSheet.create({
29966
30116
  row: {
29967
30117
  display: "flex",
29968
30118
  flexDirection: "row",
@@ -29981,17 +30131,17 @@ const EllipseSwatch = props => {
29981
30131
  } = props;
29982
30132
  return /*#__PURE__*/React.createElement(View, {
29983
30133
  "aria-label": `${color}, stroke ${strokeStyle}, fill ${fillStyle}`,
29984
- style: [styles$A.container, {
30134
+ style: [styles$B.container, {
29985
30135
  border: `4px ${strokeStyle} ${lockedFigureColors[color]}`
29986
30136
  }]
29987
30137
  }, /*#__PURE__*/React.createElement(View, {
29988
- style: [styles$A.innerCircle, {
30138
+ style: [styles$B.innerCircle, {
29989
30139
  backgroundColor: lockedFigureColors[color],
29990
30140
  opacity: fillStyle === "white" ? 0 : lockedFigureFillStyles[fillStyle]
29991
30141
  }]
29992
30142
  }));
29993
30143
  };
29994
- const styles$A = StyleSheet.create({
30144
+ const styles$B = StyleSheet.create({
29995
30145
  container: {
29996
30146
  // Add a white outline so that the color swatch is visible when
29997
30147
  // the dropdown option is highlighted with its blue background.
@@ -30017,7 +30167,7 @@ const LineStrokeSelect = props => {
30017
30167
  } = props;
30018
30168
  return /*#__PURE__*/React.createElement(LabelMedium, {
30019
30169
  tag: "label",
30020
- style: styles$z.lineStrokeSelect
30170
+ style: styles$A.lineStrokeSelect
30021
30171
  }, "stroke", /*#__PURE__*/React.createElement(Strut, {
30022
30172
  size: spacing.xxxSmall_4
30023
30173
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30034,7 +30184,7 @@ const LineStrokeSelect = props => {
30034
30184
  label: "dashed"
30035
30185
  })));
30036
30186
  };
30037
- const styles$z = StyleSheet.create({
30187
+ const styles$A = StyleSheet.create({
30038
30188
  lineStrokeSelect: {
30039
30189
  display: "flex",
30040
30190
  flexDirection: "row",
@@ -30056,40 +30206,40 @@ const LockedFigureSettingsActions = props => {
30056
30206
  onRemove
30057
30207
  } = props;
30058
30208
  return /*#__PURE__*/React.createElement(View, {
30059
- style: styles$y.container
30209
+ style: styles$z.container
30060
30210
  }, /*#__PURE__*/React.createElement(Button, {
30061
30211
  startIcon: trashIcon,
30062
30212
  "aria-label": `Delete locked ${figureType}`,
30063
30213
  onClick: onRemove,
30064
30214
  kind: "tertiary",
30065
- style: styles$y.deleteButton
30215
+ style: styles$z.deleteButton
30066
30216
  }, "Delete"), /*#__PURE__*/React.createElement(Spring, null), /*#__PURE__*/React.createElement(IconButton, {
30067
30217
  icon: caretDoubleUpIcon,
30068
30218
  size: "small",
30069
30219
  "aria-label": `Move locked ${figureType} to the back`,
30070
30220
  onClick: () => onMove("back"),
30071
- style: styles$y.iconButton
30221
+ style: styles$z.iconButton
30072
30222
  }), /*#__PURE__*/React.createElement(IconButton, {
30073
30223
  icon: caretUpIcon,
30074
30224
  size: "small",
30075
30225
  "aria-label": `Move locked ${figureType} backward`,
30076
30226
  onClick: () => onMove("backward"),
30077
- style: styles$y.iconButton
30227
+ style: styles$z.iconButton
30078
30228
  }), /*#__PURE__*/React.createElement(IconButton, {
30079
30229
  icon: caretDownIcon,
30080
30230
  size: "small",
30081
30231
  "aria-label": `Move locked ${figureType} forward`,
30082
30232
  onClick: () => onMove("forward"),
30083
- style: styles$y.iconButton
30233
+ style: styles$z.iconButton
30084
30234
  }), /*#__PURE__*/React.createElement(IconButton, {
30085
30235
  icon: caretDoubleDownIcon,
30086
30236
  size: "small",
30087
30237
  "aria-label": `Move locked ${figureType} to the front`,
30088
30238
  onClick: () => onMove("front"),
30089
- style: styles$y.iconButton
30239
+ style: styles$z.iconButton
30090
30240
  }));
30091
30241
  };
30092
- const styles$y = StyleSheet.create({
30242
+ const styles$z = StyleSheet.create({
30093
30243
  container: {
30094
30244
  width: "100%",
30095
30245
  flexDirection: "row",
@@ -30136,7 +30286,7 @@ const LockedEllipseSettings = props => {
30136
30286
  /*#__PURE__*/
30137
30287
  // Summary: Ellipse, center, radius, color (opacity, dashed)
30138
30288
  React.createElement(View, {
30139
- style: styles$x.row
30289
+ style: styles$y.row
30140
30290
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Ellipse (${center[0]}, ${center[1]}), radius ${radius[0]}, ${radius[1]}`), /*#__PURE__*/React.createElement(Strut, {
30141
30291
  size: spacing.xSmall_8
30142
30292
  }), /*#__PURE__*/React.createElement(EllipseSwatch, {
@@ -30145,19 +30295,19 @@ const LockedEllipseSettings = props => {
30145
30295
  strokeStyle: strokeStyle
30146
30296
  }))
30147
30297
  }, /*#__PURE__*/React.createElement(View, {
30148
- style: styles$x.row
30298
+ style: styles$y.row
30149
30299
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30150
30300
  coord: center,
30151
- style: styles$x.spaceUnder,
30301
+ style: styles$y.spaceUnder,
30152
30302
  onChange: newCoords => onChangeProps({
30153
30303
  center: newCoords
30154
30304
  })
30155
30305
  }), /*#__PURE__*/React.createElement(View, {
30156
- style: styles$x.spaceUnder
30306
+ style: styles$y.spaceUnder
30157
30307
  }, /*#__PURE__*/React.createElement(InfoTip$d, null, "The coordinates for the center of the ellipse."))), /*#__PURE__*/React.createElement(CoordinatePairInput, {
30158
30308
  coord: radius,
30159
30309
  labels: ["x radius", "y radius"],
30160
- style: styles$x.spaceUnder,
30310
+ style: styles$y.spaceUnder,
30161
30311
  onChange: newCoords => onChangeProps({
30162
30312
  radius: newCoords
30163
30313
  })
@@ -30169,7 +30319,7 @@ const LockedEllipseSettings = props => {
30169
30319
  }), /*#__PURE__*/React.createElement(Strut, {
30170
30320
  size: spacing.xSmall_8
30171
30321
  }), /*#__PURE__*/React.createElement(View, {
30172
- style: [styles$x.row, styles$x.spaceUnder]
30322
+ style: [styles$y.row, styles$y.spaceUnder]
30173
30323
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30174
30324
  selectedValue: color,
30175
30325
  onChange: handleColorChange
@@ -30177,7 +30327,7 @@ const LockedEllipseSettings = props => {
30177
30327
  size: spacing.medium_16
30178
30328
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30179
30329
  tag: "label",
30180
- style: [styles$x.row, styles$x.truncatedWidth]
30330
+ style: [styles$y.row, styles$y.truncatedWidth]
30181
30331
  }, "fill", /*#__PURE__*/React.createElement(Strut, {
30182
30332
  size: spacing.xxSmall_6
30183
30333
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30203,7 +30353,7 @@ const LockedEllipseSettings = props => {
30203
30353
  onRemove: onRemove
30204
30354
  }));
30205
30355
  };
30206
- const styles$x = StyleSheet.create({
30356
+ const styles$y = StyleSheet.create({
30207
30357
  row: {
30208
30358
  display: "flex",
30209
30359
  flexDirection: "row",
@@ -30224,15 +30374,15 @@ const LineSwatch = props => {
30224
30374
  lineStyle
30225
30375
  } = props;
30226
30376
  return /*#__PURE__*/React.createElement(View, {
30227
- style: styles$w.container
30377
+ style: styles$x.container
30228
30378
  }, /*#__PURE__*/React.createElement(View, {
30229
30379
  "aria-label": `${color}, ${lineStyle}`,
30230
- style: [styles$w.lineSwatch, {
30380
+ style: [styles$x.lineSwatch, {
30231
30381
  border: `5px ${lineStyle} ${lockedFigureColors[color]}`
30232
30382
  }]
30233
30383
  }));
30234
30384
  };
30235
- const styles$w = StyleSheet.create({
30385
+ const styles$x = StyleSheet.create({
30236
30386
  container: {
30237
30387
  backgroundColor: color.white,
30238
30388
  justifyContent: "center",
@@ -30244,6 +30394,12 @@ const styles$w = StyleSheet.create({
30244
30394
  }
30245
30395
  });
30246
30396
 
30397
+ const examples = {
30398
+ linear: ["x + 5", "1/2x - 2"],
30399
+ polynomial: ["1/2x^2 + 3x - 4", "(1/3)x^3 - 2x^2 + 3x - 4"],
30400
+ trigonometric: ["sin(x) * 3", "arctan(2x) + 4"]
30401
+ };
30402
+
30247
30403
  /**
30248
30404
  * LockedFunctionSettings is a component that allows the user to edit the
30249
30405
  * settings of specifically a locked function on the graph.
@@ -30262,18 +30418,20 @@ const LockedFunctionSettings = props => {
30262
30418
  onRemove
30263
30419
  } = props;
30264
30420
  const equationPrefix = directionalAxis === "x" ? "y=" : "x=";
30421
+ const domainRangeText = directionalAxis === "x" ? "domain" : "range";
30265
30422
  const lineLabel = `Function (${equationPrefix}${equation})`;
30266
30423
 
30267
- // Tracking the string value of domain constraints to handle interim state of
30424
+ // Tracking the string value of domain/range constraints to handle interim state of
30268
30425
  // entering a negative value, as well as representing Infinity as an empty string.
30269
30426
  // This variable is used when specifying the values of the input fields.
30270
30427
  const [domainEntries, setDomainEntries] = useState([domain && domain[0] !== -Infinity ? domain[0].toString() : "", domain && domain[1] !== Infinity ? domain[1].toString() : ""]);
30428
+ const [exampleCategory, setExampleCategory] = useState("");
30271
30429
  useEffect(() => {
30272
- // "useEffect" used to maintain parity between domain constraints and their string representation.
30430
+ // "useEffect" used to maintain parity between domain/range constraints and their string representation.
30273
30431
  setDomainEntries([domain && domain[0] !== -Infinity ? domain[0].toString() : "", domain && domain[1] !== Infinity ? domain[1].toString() : ""]);
30274
30432
  }, [domain]);
30275
30433
 
30276
- // Generic function for handling property changes (except for 'domain')
30434
+ // Generic function for handling property changes (except for 'domain/range')
30277
30435
  function handlePropChange(property, newValue) {
30278
30436
  const updatedProps = {};
30279
30437
  updatedProps[property] = newValue;
@@ -30281,8 +30439,8 @@ const LockedFunctionSettings = props => {
30281
30439
  }
30282
30440
 
30283
30441
  /*
30284
- Reason for having a separate 'propChange' function for 'domain':
30285
- Domain entries are optional. Their default value is +/- Infinity.
30442
+ Reason for having a separate 'propChange' function for 'domain/range':
30443
+ Domain/Range entries are optional. Their default value is +/- Infinity.
30286
30444
  Since input fields that are empty evaluate to zero, there needs to be
30287
30445
  dedicated code to convert empty to Infinity.
30288
30446
  */
@@ -30302,13 +30460,16 @@ const LockedFunctionSettings = props => {
30302
30460
  domain: newDomain
30303
30461
  });
30304
30462
  }
30463
+ const exampleCategories = Object.keys(examples);
30464
+ const exampleCategorySelected = exampleCategory !== "";
30465
+ const exampleContent = exampleCategorySelected ? examples[exampleCategory] : ["Select category to see example equations"];
30305
30466
  return /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
30306
30467
  expanded: props.expanded,
30307
30468
  onToggle: props.onToggle,
30308
30469
  header: /*#__PURE__*/React.createElement(View, {
30309
- style: styles$v.row
30470
+ style: styles$w.row
30310
30471
  }, /*#__PURE__*/React.createElement(LabelLarge, {
30311
- style: styles$v.accordionHeader
30472
+ style: styles$w.accordionHeader
30312
30473
  }, lineLabel), /*#__PURE__*/React.createElement(Strut, {
30313
30474
  size: spacing.xSmall_8
30314
30475
  }), /*#__PURE__*/React.createElement(LineSwatch, {
@@ -30316,7 +30477,7 @@ const LockedFunctionSettings = props => {
30316
30477
  lineStyle: strokeStyle
30317
30478
  }))
30318
30479
  }, /*#__PURE__*/React.createElement(View, {
30319
- style: [styles$v.row, styles$v.spaceUnder]
30480
+ style: [styles$w.row, styles$w.spaceUnder]
30320
30481
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30321
30482
  selectedValue: lineColor,
30322
30483
  onChange: newValue => {
@@ -30330,14 +30491,14 @@ const LockedFunctionSettings = props => {
30330
30491
  handlePropChange("strokeStyle", newValue);
30331
30492
  }
30332
30493
  })), /*#__PURE__*/React.createElement(View, {
30333
- style: [styles$v.row, styles$v.rowSpace]
30494
+ style: [styles$w.row, styles$w.rowSpace]
30334
30495
  }, /*#__PURE__*/React.createElement(SingleSelect, {
30335
30496
  selectedValue: directionalAxis,
30336
30497
  onChange: newValue => {
30337
30498
  handlePropChange("directionalAxis", newValue);
30338
30499
  },
30339
30500
  "aria-label": "equation prefix",
30340
- style: styles$v.equationPrefix
30501
+ style: [styles$w.dropdownLabel, styles$w.axisMenu]
30341
30502
  // Placeholder is required, but never gets used.
30342
30503
  ,
30343
30504
  placeholder: ""
@@ -30356,17 +30517,17 @@ const LockedFunctionSettings = props => {
30356
30517
  onChange: newValue => {
30357
30518
  handlePropChange("equation", newValue);
30358
30519
  },
30359
- style: [styles$v.textField]
30520
+ style: [styles$w.textField]
30360
30521
  })), /*#__PURE__*/React.createElement(View, {
30361
- style: [styles$v.row, styles$v.rowSpace]
30522
+ style: [styles$w.row, styles$w.rowSpace]
30362
30523
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30363
30524
  tag: "label",
30364
- style: styles$v.domainMin
30365
- }, "domain min", /*#__PURE__*/React.createElement(Strut, {
30525
+ style: [styles$w.dropdownLabel, styles$w.domainMin]
30526
+ }, `${domainRangeText} min`, /*#__PURE__*/React.createElement(Strut, {
30366
30527
  size: spacing.xxSmall_6
30367
30528
  }), /*#__PURE__*/React.createElement(TextField, {
30368
30529
  type: "number",
30369
- style: styles$v.domainMinField,
30530
+ style: styles$w.domainMinField,
30370
30531
  value: domainEntries[0],
30371
30532
  onChange: newValue => {
30372
30533
  handleDomainChange(0, newValue);
@@ -30375,24 +30536,83 @@ const LockedFunctionSettings = props => {
30375
30536
  size: spacing.medium_16
30376
30537
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30377
30538
  tag: "label",
30378
- "aria-label": "domain max",
30379
- style: styles$v.domainMax
30539
+ "aria-label": `${domainRangeText} max`,
30540
+ style: [styles$w.dropdownLabel, styles$w.domainMax]
30380
30541
  }, "max", /*#__PURE__*/React.createElement(Strut, {
30381
30542
  size: spacing.xxSmall_6
30382
30543
  }), /*#__PURE__*/React.createElement(TextField, {
30383
30544
  type: "number",
30384
- style: styles$v.domainMaxField,
30545
+ style: styles$w.domainMaxField,
30385
30546
  value: domainEntries[1],
30386
30547
  onChange: newValue => {
30387
30548
  handleDomainChange(1, newValue);
30388
30549
  }
30389
- }))), /*#__PURE__*/React.createElement(LockedFigureSettingsActions, {
30550
+ }))), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
30551
+ header: /*#__PURE__*/React.createElement(LabelLarge, null, "Example Functions"),
30552
+ expanded: false,
30553
+ containerStyle: styles$w.exampleWorkspace,
30554
+ panelStyle: styles$w.exampleAccordionPanel
30555
+ }, /*#__PURE__*/React.createElement(LabelMedium, {
30556
+ tag: "label",
30557
+ style: styles$w.dropdownLabel
30558
+ }, "Choose a category", /*#__PURE__*/React.createElement(Strut, {
30559
+ size: spacing.xxSmall_6
30560
+ }), /*#__PURE__*/React.createElement(SingleSelect, {
30561
+ selectedValue: exampleCategory,
30562
+ onChange: setExampleCategory,
30563
+ placeholder: "examples"
30564
+ }, exampleCategories.map(category => {
30565
+ return /*#__PURE__*/React.createElement(OptionItem, {
30566
+ key: category,
30567
+ value: category,
30568
+ label: category
30569
+ });
30570
+ }))), exampleCategorySelected && /*#__PURE__*/React.createElement("ul", {
30571
+ className: css(styles$w.exampleContainer)
30572
+ }, exampleContent.map((example, index) => /*#__PURE__*/React.createElement(ExampleItem, {
30573
+ category: exampleCategory,
30574
+ example: example,
30575
+ index: index,
30576
+ pasteEquationFn: handlePropChange
30577
+ })))), /*#__PURE__*/React.createElement(LockedFigureSettingsActions, {
30390
30578
  figureType: props.type,
30391
30579
  onMove: onMove,
30392
30580
  onRemove: onRemove
30393
30581
  }));
30394
30582
  };
30395
- const styles$v = StyleSheet.create({
30583
+ const ExampleItem = props => {
30584
+ const {
30585
+ category,
30586
+ example,
30587
+ index,
30588
+ pasteEquationFn
30589
+ } = props;
30590
+ const exampleId = useId();
30591
+ return /*#__PURE__*/React.createElement("li", {
30592
+ key: `${category}-${index}`,
30593
+ className: css(styles$w.exampleRow)
30594
+ }, /*#__PURE__*/React.createElement(IconButton, {
30595
+ icon: autoPasteIcon,
30596
+ "aria-label": "paste example",
30597
+ "aria-describedby": exampleId,
30598
+ onClick: () => pasteEquationFn("equation", example),
30599
+ size: "medium",
30600
+ style: styles$w.copyPasteButton
30601
+ }), /*#__PURE__*/React.createElement(IconButton, {
30602
+ icon: copyIcon,
30603
+ "aria-label": "copy example",
30604
+ "aria-describedby": exampleId,
30605
+ onClick: () => navigator.clipboard.writeText(example),
30606
+ size: "medium",
30607
+ style: styles$w.copyPasteButton
30608
+ }), /*#__PURE__*/React.createElement(Strut, {
30609
+ size: spacing.xxxSmall_4
30610
+ }), /*#__PURE__*/React.createElement(View, {
30611
+ style: styles$w.exampleContent,
30612
+ id: exampleId
30613
+ }, example));
30614
+ };
30615
+ const styles$w = StyleSheet.create({
30396
30616
  accordionHeader: {
30397
30617
  textOverflow: "ellipsis",
30398
30618
  // A maximum width needs to be specified in order for the ellipsis to work.
@@ -30402,17 +30622,20 @@ const styles$v = StyleSheet.create({
30402
30622
  overflow: "hidden",
30403
30623
  whiteSpace: "nowrap"
30404
30624
  },
30405
- equationPrefix: {
30625
+ axisMenu: {
30406
30626
  minWidth: "auto"
30407
30627
  },
30628
+ copyPasteButton: {
30629
+ flexShrink: "0",
30630
+ margin: "0 2px"
30631
+ },
30408
30632
  domainMin: {
30409
- alignItems: "center",
30410
- display: "flex",
30633
+ justifyContent: "space-between",
30411
30634
  // The 'width' property is applied to the label, which wraps the text and the input field.
30412
30635
  // The width of the input fields (min/max) should be the same (to have a consistent look),
30413
30636
  // 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).
30637
+ // For the "domain/range min" block, the text is 82.7px, and the strut is 6px (88.7px total).
30638
+ // The "domain/range max" block is 30.23px, and the strut is 6px (36.23px total).
30416
30639
  // The calculation takes the remain space after the text & struts (141px total) are removed,
30417
30640
  // and divides it between the two input fields equally.
30418
30641
  // The calculation reads: "Take 1/2 of the non-text space, and add the required space for this label's text"
@@ -30425,8 +30648,6 @@ const styles$v = StyleSheet.create({
30425
30648
  },
30426
30649
 
30427
30650
  domainMax: {
30428
- alignItems: "center",
30429
- display: "flex",
30430
30651
  // See explanation for "domainMin" for the calculation below.
30431
30652
  width: "calc(((100% - 141px) / 2) + 36.2px)"
30432
30653
  },
@@ -30434,6 +30655,44 @@ const styles$v = StyleSheet.create({
30434
30655
  width: "calc(100% - 36.2px)" // make room for the label
30435
30656
  },
30436
30657
 
30658
+ dropdownLabel: {
30659
+ alignItems: "center",
30660
+ display: "flex"
30661
+ },
30662
+ exampleAccordionPanel: {
30663
+ alignItems: "start",
30664
+ paddingBottom: "12px",
30665
+ flexDirection: "row",
30666
+ flexWrap: "wrap"
30667
+ },
30668
+ exampleContainer: {
30669
+ background: "white",
30670
+ border: `1px solid ${color.fadedOffBlack16}`,
30671
+ borderRadius: "4px",
30672
+ flexGrow: "1",
30673
+ listStyleType: "none",
30674
+ // Nothing special about the maxHeight value,
30675
+ // just a good height to partially show a 3rd example in the list
30676
+ // to hint at scrollable content.
30677
+ maxHeight: "88px",
30678
+ margin: "8px 0 0 0",
30679
+ overflowY: "scroll",
30680
+ padding: "4px 12px 4px 4px"
30681
+ },
30682
+ exampleContent: {
30683
+ fontFamily: `"Lato", sans-serif`,
30684
+ flexGrow: "1",
30685
+ color: color.offBlack
30686
+ },
30687
+ exampleRow: {
30688
+ alignItems: "center",
30689
+ display: "flex",
30690
+ flexDirection: "row",
30691
+ minHeight: "44px"
30692
+ },
30693
+ exampleWorkspace: {
30694
+ background: color.white50
30695
+ },
30437
30696
  rowSpace: {
30438
30697
  marginTop: spacing.xSmall_8
30439
30698
  },
@@ -30471,14 +30730,14 @@ function LockedLabelSettings(props) {
30471
30730
  expanded: expanded,
30472
30731
  onToggle: onToggle,
30473
30732
  header: /*#__PURE__*/React.createElement(View, {
30474
- style: [styles$u.row, styles$u.accordionHeaderContainer]
30733
+ style: [styles$v.row, styles$v.accordionHeaderContainer]
30475
30734
  }, /*#__PURE__*/React.createElement(LabelLarge, null, "Label (", coord[0], ", ", coord[1], ")"), /*#__PURE__*/React.createElement(Strut, {
30476
30735
  size: spacing.xSmall_8
30477
30736
  }), text !== "" && /*#__PURE__*/React.createElement(LabelLarge, {
30478
30737
  style: [{
30479
30738
  backgroundColor: color.white,
30480
30739
  color: lockedFigureColors[color$1]
30481
- }, styles$u.accordionHeader]
30740
+ }, styles$v.accordionHeader]
30482
30741
  }, text))
30483
30742
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30484
30743
  coord: coord,
@@ -30487,10 +30746,10 @@ function LockedLabelSettings(props) {
30487
30746
  coord: newCoords
30488
30747
  });
30489
30748
  },
30490
- style: styles$u.spaceUnder
30749
+ style: styles$v.spaceUnder
30491
30750
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30492
30751
  tag: "label",
30493
- style: [styles$u.row, styles$u.spaceUnder]
30752
+ style: [styles$v.row, styles$v.spaceUnder]
30494
30753
  }, "TeX", /*#__PURE__*/React.createElement(Strut, {
30495
30754
  size: spacing.xSmall_8
30496
30755
  }), /*#__PURE__*/React.createElement(TextField, {
@@ -30500,7 +30759,7 @@ function LockedLabelSettings(props) {
30500
30759
  text: newValue
30501
30760
  })
30502
30761
  })), /*#__PURE__*/React.createElement(View, {
30503
- style: styles$u.row
30762
+ style: styles$v.row
30504
30763
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30505
30764
  selectedValue: color$1,
30506
30765
  onChange: newColor => {
@@ -30508,12 +30767,12 @@ function LockedLabelSettings(props) {
30508
30767
  color: newColor
30509
30768
  });
30510
30769
  },
30511
- style: styles$u.spaceUnder
30770
+ style: styles$v.spaceUnder
30512
30771
  }), /*#__PURE__*/React.createElement(Strut, {
30513
30772
  size: spacing.medium_16
30514
30773
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30515
30774
  tag: "label",
30516
- style: styles$u.row
30775
+ style: styles$v.row
30517
30776
  }, "size", /*#__PURE__*/React.createElement(Strut, {
30518
30777
  size: spacing.xSmall_8
30519
30778
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30540,7 +30799,7 @@ function LockedLabelSettings(props) {
30540
30799
  onRemove: onRemove
30541
30800
  }));
30542
30801
  }
30543
- const styles$u = StyleSheet.create({
30802
+ const styles$v = StyleSheet.create({
30544
30803
  accordionHeaderContainer: {
30545
30804
  // Stop the label summary from wrapping.
30546
30805
  whiteSpace: "nowrap"
@@ -30574,7 +30833,7 @@ const LabeledSwitch = props => {
30574
30833
  const ids = useUniqueIdWithMock();
30575
30834
  const switchId = ids.get("switch");
30576
30835
  return /*#__PURE__*/React.createElement(View, {
30577
- style: [styles$t.row, style]
30836
+ style: [styles$u.row, style]
30578
30837
  }, /*#__PURE__*/React.createElement(Switch, {
30579
30838
  id: switchId,
30580
30839
  checked: checked,
@@ -30586,7 +30845,7 @@ const LabeledSwitch = props => {
30586
30845
  htmlFor: switchId
30587
30846
  }, label));
30588
30847
  };
30589
- const styles$t = StyleSheet.create({
30848
+ const styles$u = StyleSheet.create({
30590
30849
  row: {
30591
30850
  flexDirection: "row",
30592
30851
  alignItems: "center"
@@ -30620,13 +30879,13 @@ const DefiningPointSettings = props => {
30620
30879
  return /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
30621
30880
  expanded: expanded,
30622
30881
  onToggle: onToggle,
30623
- containerStyle: styles$s.container,
30624
- panelStyle: styles$s.accordionPanel,
30882
+ containerStyle: styles$t.container,
30883
+ panelStyle: styles$t.accordionPanel,
30625
30884
  header:
30626
30885
  /*#__PURE__*/
30627
30886
  // Summary: Point, coords, color (filled/open)
30628
30887
  React.createElement(View, {
30629
- style: styles$s.row
30888
+ style: styles$t.row
30630
30889
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `${label || "Point"} (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React.createElement(Strut, {
30631
30890
  size: spacing.xSmall_8
30632
30891
  }), showPoint && /*#__PURE__*/React.createElement(ColorSwatch, {
@@ -30636,7 +30895,7 @@ const DefiningPointSettings = props => {
30636
30895
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30637
30896
  coord: coord,
30638
30897
  error: !!error,
30639
- style: styles$s.spaceUnder,
30898
+ style: styles$t.spaceUnder,
30640
30899
  onChange: newCoords => {
30641
30900
  onChangeProps({
30642
30901
  coord: newCoords
@@ -30645,12 +30904,12 @@ const DefiningPointSettings = props => {
30645
30904
  }), onTogglePoint && /*#__PURE__*/React.createElement(LabeledSwitch, {
30646
30905
  label: "show point on graph",
30647
30906
  checked: !!showPoint,
30648
- style: showPoint && styles$s.spaceUnder,
30907
+ style: showPoint && styles$t.spaceUnder,
30649
30908
  onChange: onTogglePoint
30650
30909
  }), showPoint && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColorSelect, {
30651
30910
  selectedValue: pointColor,
30652
30911
  onChange: handleColorChange,
30653
- style: styles$s.spaceUnder
30912
+ style: styles$t.spaceUnder
30654
30913
  }), /*#__PURE__*/React.createElement(LabeledSwitch, {
30655
30914
  label: "open point",
30656
30915
  checked: !filled,
@@ -30661,7 +30920,7 @@ const DefiningPointSettings = props => {
30661
30920
  }
30662
30921
  })));
30663
30922
  };
30664
- const styles$s = StyleSheet.create({
30923
+ const styles$t = StyleSheet.create({
30665
30924
  container: {
30666
30925
  marginTop: spacing.xSmall_8,
30667
30926
  marginBottom: 0,
@@ -30724,7 +30983,7 @@ const LockedLineSettings = props => {
30724
30983
  expanded: props.expanded,
30725
30984
  onToggle: props.onToggle,
30726
30985
  header: /*#__PURE__*/React.createElement(View, {
30727
- style: styles$r.row
30986
+ style: styles$s.row
30728
30987
  }, /*#__PURE__*/React.createElement(LabelLarge, null, lineLabel), /*#__PURE__*/React.createElement(Strut, {
30729
30988
  size: spacing.xSmall_8
30730
30989
  }), /*#__PURE__*/React.createElement(LineSwatch, {
@@ -30733,7 +30992,7 @@ const LockedLineSettings = props => {
30733
30992
  }))
30734
30993
  }, /*#__PURE__*/React.createElement(LabelMedium, {
30735
30994
  tag: "label",
30736
- style: [styles$r.row, styles$r.spaceUnder]
30995
+ style: [styles$s.row, styles$s.spaceUnder]
30737
30996
  }, "kind", /*#__PURE__*/React.createElement(Strut, {
30738
30997
  size: spacing.xxxSmall_4
30739
30998
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -30754,7 +31013,7 @@ const LockedLineSettings = props => {
30754
31013
  value: "segment",
30755
31014
  label: "segment"
30756
31015
  }))), /*#__PURE__*/React.createElement(View, {
30757
- style: styles$r.row
31016
+ style: styles$s.row
30758
31017
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30759
31018
  selectedValue: lineColor,
30760
31019
  onChange: handleColorChange
@@ -30766,7 +31025,7 @@ const LockedLineSettings = props => {
30766
31025
  lineStyle: value
30767
31026
  })
30768
31027
  })), isInvalid && /*#__PURE__*/React.createElement(LabelMedium, {
30769
- style: styles$r.errorText
31028
+ style: styles$s.errorText
30770
31029
  }, lengthZeroStr), /*#__PURE__*/React.createElement(DefiningPointSettings, _extends({
30771
31030
  label: "Point 1",
30772
31031
  showPoint: showPoint1,
@@ -30791,7 +31050,7 @@ const LockedLineSettings = props => {
30791
31050
  onRemove: onRemove
30792
31051
  }));
30793
31052
  };
30794
- const styles$r = StyleSheet.create({
31053
+ const styles$s = StyleSheet.create({
30795
31054
  row: {
30796
31055
  display: "flex",
30797
31056
  flexDirection: "row",
@@ -30832,7 +31091,7 @@ const LockedPointSettings = props => {
30832
31091
  /*#__PURE__*/
30833
31092
  // Summary: Point, coords, color (filled/open)
30834
31093
  React.createElement(View, {
30835
- style: styles$q.row
31094
+ style: styles$r.row
30836
31095
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Point (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React.createElement(Strut, {
30837
31096
  size: spacing.xSmall_8
30838
31097
  }), /*#__PURE__*/React.createElement(ColorSwatch, {
@@ -30841,7 +31100,7 @@ const LockedPointSettings = props => {
30841
31100
  }))
30842
31101
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
30843
31102
  coord: coord,
30844
- style: styles$q.spaceUnder,
31103
+ style: styles$r.spaceUnder,
30845
31104
  onChange: newCoord => {
30846
31105
  onChangeProps({
30847
31106
  coord: newCoord
@@ -30850,7 +31109,7 @@ const LockedPointSettings = props => {
30850
31109
  }), /*#__PURE__*/React.createElement(ColorSelect, {
30851
31110
  selectedValue: pointColor,
30852
31111
  onChange: handleColorChange,
30853
- style: styles$q.spaceUnder
31112
+ style: styles$r.spaceUnder
30854
31113
  }), /*#__PURE__*/React.createElement(LabeledSwitch, {
30855
31114
  label: "open point",
30856
31115
  checked: !filled,
@@ -30865,7 +31124,7 @@ const LockedPointSettings = props => {
30865
31124
  onRemove: onRemove
30866
31125
  }));
30867
31126
  };
30868
- const styles$q = StyleSheet.create({
31127
+ const styles$r = StyleSheet.create({
30869
31128
  row: {
30870
31129
  flexDirection: "row",
30871
31130
  alignItems: "center"
@@ -30883,17 +31142,17 @@ const PolygonSwatch = props => {
30883
31142
  } = props;
30884
31143
  return /*#__PURE__*/React.createElement(View, {
30885
31144
  "aria-label": `${color}, stroke ${strokeStyle}, fill ${fillStyle}`,
30886
- style: [styles$p.container, {
31145
+ style: [styles$q.container, {
30887
31146
  border: `4px ${strokeStyle} ${lockedFigureColors[color]}`
30888
31147
  }]
30889
31148
  }, /*#__PURE__*/React.createElement(View, {
30890
- style: [styles$p.innerSquare, {
31149
+ style: [styles$q.innerSquare, {
30891
31150
  backgroundColor: lockedFigureColors[color],
30892
31151
  opacity: fillStyle === "white" ? 0 : lockedFigureFillStyles[fillStyle]
30893
31152
  }]
30894
31153
  }));
30895
31154
  };
30896
- const styles$p = StyleSheet.create({
31155
+ const styles$q = StyleSheet.create({
30897
31156
  container: {
30898
31157
  // Add a white outline so that the color swatch is visible when
30899
31158
  // the dropdown option is highlighted with its blue background.
@@ -30959,7 +31218,7 @@ const LockedPolygonSettings = props => {
30959
31218
  /*#__PURE__*/
30960
31219
  // Summary: Polygon, number of sides, style swatch
30961
31220
  React.createElement(View, {
30962
- style: styles$o.row
31221
+ style: styles$p.row
30963
31222
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Polygon, ${points.length} sides`), /*#__PURE__*/React.createElement(Strut, {
30964
31223
  size: spacing.xSmall_8
30965
31224
  }), /*#__PURE__*/React.createElement(PolygonSwatch, {
@@ -30968,7 +31227,7 @@ const LockedPolygonSettings = props => {
30968
31227
  strokeStyle: strokeStyle
30969
31228
  }))
30970
31229
  }, /*#__PURE__*/React.createElement(View, {
30971
- style: [styles$o.row, styles$o.spaceUnder]
31230
+ style: [styles$p.row, styles$p.spaceUnder]
30972
31231
  }, /*#__PURE__*/React.createElement(ColorSelect, {
30973
31232
  selectedValue: color,
30974
31233
  onChange: handleColorChange
@@ -30976,7 +31235,7 @@ const LockedPolygonSettings = props => {
30976
31235
  size: spacing.medium_16
30977
31236
  }), /*#__PURE__*/React.createElement(LabelMedium, {
30978
31237
  tag: "label",
30979
- style: [styles$o.row, styles$o.truncatedWidth]
31238
+ style: [styles$p.row, styles$p.truncatedWidth]
30980
31239
  }, "fill", /*#__PURE__*/React.createElement(Strut, {
30981
31240
  size: spacing.xxSmall_6
30982
31241
  }), /*#__PURE__*/React.createElement(SingleSelect, {
@@ -31002,17 +31261,17 @@ const LockedPolygonSettings = props => {
31002
31261
  onChange: newValue => onChangeProps({
31003
31262
  showVertices: newValue
31004
31263
  }),
31005
- style: styles$o.spaceUnder
31264
+ style: styles$p.spaceUnder
31006
31265
  }), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31007
31266
  header: /*#__PURE__*/React.createElement(LabelLarge, null, "Points"),
31008
31267
  expanded: true,
31009
- containerStyle: styles$o.pointAccordionContainer,
31010
- panelStyle: styles$o.pointAccordionPanel
31268
+ containerStyle: styles$p.pointAccordionContainer,
31269
+ panelStyle: styles$p.pointAccordionPanel
31011
31270
  }, points.map((point, index) => {
31012
31271
  const pointLabel = String.fromCharCode(65 + index);
31013
31272
  return /*#__PURE__*/React.createElement(View, {
31014
31273
  key: `locked-polygon-point-index-${index}`,
31015
- style: [styles$o.row, styles$o.spaceUnder]
31274
+ style: [styles$p.row, styles$p.spaceUnder]
31016
31275
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `${pointLabel}:`), /*#__PURE__*/React.createElement(Strut, {
31017
31276
  size: spacing.medium_16
31018
31277
  }), /*#__PURE__*/React.createElement(CoordinatePairInput, {
@@ -31040,10 +31299,10 @@ const LockedPolygonSettings = props => {
31040
31299
  points: newPoints
31041
31300
  });
31042
31301
  },
31043
- style: styles$o.icon
31302
+ style: styles$p.icon
31044
31303
  }));
31045
31304
  }), /*#__PURE__*/React.createElement(View, {
31046
- style: [styles$o.row, styles$o.polygonActionsContainer]
31305
+ style: [styles$p.row, styles$p.polygonActionsContainer]
31047
31306
  }, /*#__PURE__*/React.createElement(Button, {
31048
31307
  kind: "tertiary",
31049
31308
  startIcon: plusCircle,
@@ -31053,30 +31312,30 @@ const LockedPolygonSettings = props => {
31053
31312
  });
31054
31313
  }
31055
31314
  }, "Add point"), /*#__PURE__*/React.createElement(Spring, null), /*#__PURE__*/React.createElement(View, {
31056
- style: styles$o.movementButtonsContainer
31315
+ style: styles$p.movementButtonsContainer
31057
31316
  }, /*#__PURE__*/React.createElement(IconButton, {
31058
31317
  "aria-label": "Move polygon up",
31059
- style: styles$o.iconButton,
31318
+ style: styles$p.iconButton,
31060
31319
  size: "small",
31061
31320
  icon: arrowFatUp,
31062
31321
  onClick: () => handlePolygonMove("up")
31063
31322
  }), /*#__PURE__*/React.createElement(View, {
31064
- style: styles$o.row
31323
+ style: styles$p.row
31065
31324
  }, /*#__PURE__*/React.createElement(IconButton, {
31066
31325
  "aria-label": "Move polygon left",
31067
- style: styles$o.iconButton,
31326
+ style: styles$p.iconButton,
31068
31327
  size: "small",
31069
31328
  icon: arrowFatLeft,
31070
31329
  onClick: () => handlePolygonMove("left")
31071
31330
  }), /*#__PURE__*/React.createElement(IconButton, {
31072
31331
  "aria-label": "Move polygon down",
31073
- style: styles$o.iconButton,
31332
+ style: styles$p.iconButton,
31074
31333
  size: "small",
31075
31334
  icon: arrowFatDown,
31076
31335
  onClick: () => handlePolygonMove("down")
31077
31336
  }), /*#__PURE__*/React.createElement(IconButton, {
31078
31337
  "aria-label": "Move polygon right",
31079
- style: styles$o.iconButton,
31338
+ style: styles$p.iconButton,
31080
31339
  size: "small",
31081
31340
  icon: arrowFatRight,
31082
31341
  onClick: () => handlePolygonMove("right")
@@ -31086,7 +31345,7 @@ const LockedPolygonSettings = props => {
31086
31345
  onRemove: onRemove
31087
31346
  }));
31088
31347
  };
31089
- const styles$o = StyleSheet.create({
31348
+ const styles$p = StyleSheet.create({
31090
31349
  row: {
31091
31350
  display: "flex",
31092
31351
  flexDirection: "row",
@@ -31160,7 +31419,7 @@ const LockedVectorSettings = props => {
31160
31419
  expanded: props.expanded,
31161
31420
  onToggle: props.onToggle,
31162
31421
  header: /*#__PURE__*/React.createElement(View, {
31163
- style: styles$n.row
31422
+ style: styles$o.row
31164
31423
  }, /*#__PURE__*/React.createElement(LabelLarge, null, lineLabel), /*#__PURE__*/React.createElement(Strut, {
31165
31424
  size: spacing.xSmall_8
31166
31425
  }), /*#__PURE__*/React.createElement(LineSwatch, {
@@ -31168,19 +31427,19 @@ const LockedVectorSettings = props => {
31168
31427
  lineStyle: "solid"
31169
31428
  }))
31170
31429
  }, /*#__PURE__*/React.createElement(View, {
31171
- style: [styles$n.row, styles$n.spaceUnder]
31430
+ style: [styles$o.row, styles$o.spaceUnder]
31172
31431
  }, /*#__PURE__*/React.createElement(ColorSelect, {
31173
31432
  selectedValue: lineColor,
31174
31433
  onChange: handleColorChange
31175
31434
  })), isInvalid && /*#__PURE__*/React.createElement(LabelMedium, {
31176
- style: styles$n.errorText
31435
+ style: styles$o.errorText
31177
31436
  }, lengthErrorMessage), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31178
31437
  expanded: true // Initial state is expanded
31179
31438
  ,
31180
- containerStyle: styles$n.container,
31181
- panelStyle: styles$n.accordionPanel,
31439
+ containerStyle: styles$o.container,
31440
+ panelStyle: styles$o.accordionPanel,
31182
31441
  header: /*#__PURE__*/React.createElement(View, {
31183
- style: styles$n.row
31442
+ style: styles$o.row
31184
31443
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Tail (${tail[0]}, ${tail[1]})`))
31185
31444
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31186
31445
  coord: tail,
@@ -31191,10 +31450,10 @@ const LockedVectorSettings = props => {
31191
31450
  })), /*#__PURE__*/React.createElement(PerseusEditorAccordion, {
31192
31451
  expanded: true // Initial state is expanded
31193
31452
  ,
31194
- containerStyle: styles$n.container,
31195
- panelStyle: styles$n.accordionPanel,
31453
+ containerStyle: styles$o.container,
31454
+ panelStyle: styles$o.accordionPanel,
31196
31455
  header: /*#__PURE__*/React.createElement(View, {
31197
- style: styles$n.row
31456
+ style: styles$o.row
31198
31457
  }, /*#__PURE__*/React.createElement(LabelLarge, null, `Tip (${tip[0]}, ${tip[1]})`))
31199
31458
  }, /*#__PURE__*/React.createElement(CoordinatePairInput, {
31200
31459
  coord: tip,
@@ -31208,7 +31467,7 @@ const LockedVectorSettings = props => {
31208
31467
  onRemove: onRemove
31209
31468
  }));
31210
31469
  };
31211
- const styles$n = StyleSheet.create({
31470
+ const styles$o = StyleSheet.create({
31212
31471
  accordionPanel: {
31213
31472
  paddingBottom: spacing.medium_16
31214
31473
  },
@@ -31379,7 +31638,7 @@ const LockedFiguresSection = props => {
31379
31638
  onRemove: () => removeLockedFigure(index)
31380
31639
  }));
31381
31640
  }), /*#__PURE__*/React.createElement(View, {
31382
- style: styles$m.buttonContainer
31641
+ style: styles$n.buttonContainer
31383
31642
  }, /*#__PURE__*/React.createElement(LockedFigureSelect, {
31384
31643
  showM2bFeatures: props.showM2bFeatures,
31385
31644
  showLabelsFlag: props.showLabelsFlag,
@@ -31390,10 +31649,10 @@ const LockedFiguresSection = props => {
31390
31649
  }), showExpandButton && /*#__PURE__*/React.createElement(Button, {
31391
31650
  kind: "secondary",
31392
31651
  onClick: () => toggleExpanded(allCollapsed),
31393
- style: styles$m.button
31652
+ style: styles$n.button
31394
31653
  }, buttonLabel))));
31395
31654
  };
31396
- const styles$m = StyleSheet.create({
31655
+ const styles$n = StyleSheet.create({
31397
31656
  buttonContainer: {
31398
31657
  flexDirection: "row",
31399
31658
  alignItems: "center"
@@ -31428,7 +31687,7 @@ const GraphPointsCountSelector = ({
31428
31687
  // Never uses placeholder, always has value
31429
31688
  ,
31430
31689
  placeholder: "",
31431
- style: styles$l.singleSelectShort
31690
+ style: styles$m.singleSelectShort
31432
31691
  }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React.createElement(OptionItem, {
31433
31692
  key: n,
31434
31693
  value: `${n}`,
@@ -31439,7 +31698,7 @@ const GraphPointsCountSelector = ({
31439
31698
  label: "unlimited"
31440
31699
  })]);
31441
31700
  };
31442
- const styles$l = StyleSheet.create({
31701
+ const styles$m = StyleSheet.create({
31443
31702
  singleSelectShort: {
31444
31703
  // Non-standard spacing, but it's the smallest we can go
31445
31704
  // without running into styling issues with the dropdown.
@@ -31452,7 +31711,7 @@ const GraphTypeSelector = props => {
31452
31711
  selectedValue: props.graphType,
31453
31712
  onChange: props.onChange,
31454
31713
  placeholder: "Select a graph type",
31455
- style: styles$k.singleSelectShort
31714
+ style: styles$l.singleSelectShort
31456
31715
  }, /*#__PURE__*/React.createElement(OptionItem, {
31457
31716
  value: "linear",
31458
31717
  label: "Linear function"
@@ -31485,7 +31744,7 @@ const GraphTypeSelector = props => {
31485
31744
  label: "Angle"
31486
31745
  }));
31487
31746
  };
31488
- const styles$k = StyleSheet.create({
31747
+ const styles$l = StyleSheet.create({
31489
31748
  singleSelectShort: {
31490
31749
  // Non-standard spacing, but it's the smallest we can go
31491
31750
  // without running into styling issues with the dropdown.
@@ -31516,6 +31775,54 @@ function InteractiveGraphCorrectAnswer(props) {
31516
31775
  }, props.equationString)), props.children));
31517
31776
  }
31518
31777
 
31778
+ function InteractiveGraphDescription(props) {
31779
+ const {
31780
+ ariaLabelValue,
31781
+ ariaDescriptionValue,
31782
+ onChange
31783
+ } = props;
31784
+ const [isOpen, setIsOpen] = React.useState(true);
31785
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Heading, {
31786
+ title: "Description",
31787
+ isCollapsible: true,
31788
+ isOpen: isOpen,
31789
+ onToggle: setIsOpen
31790
+ }), isOpen && /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(LabelXSmall, {
31791
+ style: styles$k.caption
31792
+ }, "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, {
31793
+ tag: "label"
31794
+ }, "Title", /*#__PURE__*/React.createElement(TextField, {
31795
+ value: ariaLabelValue,
31796
+ onChange: newValue => onChange({
31797
+ fullGraphAriaLabel: newValue
31798
+ })
31799
+ })), /*#__PURE__*/React.createElement(Strut, {
31800
+ size: spacing.xSmall_8
31801
+ }), /*#__PURE__*/React.createElement(LabelLarge, {
31802
+ tag: "label"
31803
+ // TODO(LEMS-2332): Remove this style prop after
31804
+ // switching to WB TextArea
31805
+ ,
31806
+ style: {
31807
+ display: "flex",
31808
+ flexDirection: "column"
31809
+ }
31810
+ }, "Description", /*#__PURE__*/React.createElement("textarea", {
31811
+ rows: 8,
31812
+ value: ariaDescriptionValue,
31813
+ onChange: e => onChange({
31814
+ fullGraphAriaDescription: e.target.value
31815
+ })
31816
+ }))));
31817
+ }
31818
+ const styles$k = StyleSheet.create({
31819
+ caption: {
31820
+ color: color.offBlack64,
31821
+ paddingTop: spacing.xxSmall_6,
31822
+ paddingBottom: spacing.xxSmall_6
31823
+ }
31824
+ });
31825
+
31519
31826
  const {
31520
31827
  ButtonGroup: ButtonGroup$2,
31521
31828
  InfoTip: InfoTip$c,
@@ -32443,18 +32750,10 @@ const {
32443
32750
  containerSizeClass,
32444
32751
  getInteractiveBoxFromSizeClass
32445
32752
  } = SizingUtils;
32446
- const DeprecationMixin = Util.DeprecationMixin;
32447
32753
  const InteractiveGraph = InteractiveGraphWidget.widget;
32448
32754
  const defaultBackgroundImage = {
32449
32755
  url: null
32450
32756
  };
32451
- const deprecatedProps = {
32452
- showGraph: function (props) {
32453
- return {
32454
- markings: props.showGraph ? "graph" : "none"
32455
- };
32456
- }
32457
- };
32458
32757
  const POLYGON_SIDES = _.map(_.range(3, 13), function (value) {
32459
32758
  return /*#__PURE__*/React.createElement(OptionItem, {
32460
32759
  key: `polygon-sides-${value}`,
@@ -32473,16 +32772,6 @@ class InteractiveGraphEditor extends React.Component {
32473
32772
  super(...args);
32474
32773
  this.displayName = "InteractiveGraphEditor";
32475
32774
  this.className = "perseus-widget-interactive-graph";
32476
- // TODO(jack): Use versioning instead of DeprecationMixin
32477
- this.deprecatedProps = deprecatedProps;
32478
- this.changeMatchType = newValue => {
32479
- const correct = _extends({}, this.props.correct, {
32480
- match: newValue
32481
- });
32482
- this.props.onChange({
32483
- correct: correct
32484
- });
32485
- };
32486
32775
  this.changeStartCoords = coords => {
32487
32776
  var _this$props$graph;
32488
32777
  if (!((_this$props$graph = this.props.graph) != null && _this$props$graph.type)) {
@@ -32508,16 +32797,12 @@ class InteractiveGraphEditor extends React.Component {
32508
32797
  return issues;
32509
32798
  };
32510
32799
  }
32511
- // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
32512
- UNSAFE_componentWillMount() {
32513
- DeprecationMixin.UNSAFE_componentWillMount.call(this);
32514
- }
32515
32800
  // serialize() is what makes copy/paste work. All the properties included
32516
32801
  // in the serialization json are included when, for example, a graph
32517
32802
  // is copied from the question editor and pasted into the hint editor
32518
32803
  // (double brackets in the markdown).
32519
32804
  serialize() {
32520
- const json = _.pick(this.props, "step", "backgroundImage", "markings", "labels", "showProtractor", "showTooltips", "range", "gridStep", "snapStep", "lockedFigures");
32805
+ const json = _.pick(this.props, "step", "backgroundImage", "markings", "labels", "showProtractor", "showTooltips", "range", "gridStep", "snapStep", "lockedFigures", "fullGraphAriaLabel", "fullGraphAriaDescription");
32521
32806
 
32522
32807
  // eslint-disable-next-line react/no-string-refs
32523
32808
  const graph = this.refs.graph;
@@ -32543,7 +32828,7 @@ class InteractiveGraphEditor extends React.Component {
32543
32828
  return json;
32544
32829
  }
32545
32830
  render() {
32546
- 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;
32831
+ 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, _this$props$apiOption4;
32547
32832
  let graph;
32548
32833
  let equationString;
32549
32834
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, interactiveSizes.defaultBoxSize);
@@ -32567,15 +32852,21 @@ class InteractiveGraphEditor extends React.Component {
32567
32852
  showProtractor: this.props.showProtractor,
32568
32853
  showTooltips: this.props.showTooltips,
32569
32854
  lockedFigures: this.props.lockedFigures,
32855
+ fullGraphAriaLabel: this.props.fullGraphAriaLabel,
32856
+ fullGraphAriaDescription: this.props.fullGraphAriaDescription,
32570
32857
  trackInteraction: function () {},
32571
- onChange: newProps => {
32858
+ onChange: ({
32859
+ graph: newGraph
32860
+ }) => {
32572
32861
  let correct = this.props.correct;
32573
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
32574
- if (correct.type === newProps.graph.type) {
32575
- correct = _extends({}, correct, newProps.graph);
32862
+ // TODO(benchristel): can we improve the type of onChange
32863
+ // so this invariant isn't necessary?
32864
+ invariant(newGraph != null);
32865
+ if (correct.type === newGraph.type) {
32866
+ correct = mergeGraphs(correct, newGraph);
32576
32867
  } else {
32577
32868
  // Clear options from previous graph
32578
- correct = newProps.graph;
32869
+ correct = newGraph;
32579
32870
  }
32580
32871
  this.props.onChange({
32581
32872
  correct: correct,
@@ -32618,7 +32909,11 @@ class InteractiveGraphEditor extends React.Component {
32618
32909
  }
32619
32910
  });
32620
32911
  }
32621
- })), /*#__PURE__*/React.createElement(InteractiveGraphCorrectAnswer, {
32912
+ })), 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, {
32913
+ ariaLabelValue: (_this$props$fullGraph = this.props.fullGraphAriaLabel) != null ? _this$props$fullGraph : "",
32914
+ ariaDescriptionValue: (_this$props$fullGraph2 = this.props.fullGraphAriaDescription) != null ? _this$props$fullGraph2 : "",
32915
+ onChange: this.props.onChange
32916
+ }), /*#__PURE__*/React.createElement(InteractiveGraphCorrectAnswer, {
32622
32917
  equationString: equationString
32623
32918
  }, graph), ((_this$props$correct = this.props.correct) == null ? void 0 : _this$props$correct.type) === "point" && /*#__PURE__*/React.createElement(LabeledRow, {
32624
32919
  label: "Number of Points:"
@@ -32643,17 +32938,19 @@ class InteractiveGraphEditor extends React.Component {
32643
32938
  selectedValue: (_this$props$correct4 = this.props.correct) != null && _this$props$correct4.numSides ? `${this.props.correct.numSides}` : "3",
32644
32939
  placeholder: "",
32645
32940
  onChange: newValue => {
32646
- const graph = _extends({}, this.props.correct, {
32941
+ var _this$props$graph4;
32942
+ invariant(((_this$props$graph4 = this.props.graph) == null ? void 0 : _this$props$graph4.type) === "polygon");
32943
+ const updates = {
32647
32944
  numSides: parsePointCount(newValue),
32648
32945
  coords: null,
32649
32946
  // reset the snap for UNLIMITED, which
32650
32947
  // only supports "grid"
32651
32948
  // From: D6578
32652
32949
  snapTo: "grid"
32653
- });
32950
+ };
32654
32951
  this.props.onChange({
32655
- correct: graph,
32656
- graph: graph
32952
+ correct: _extends({}, this.props.correct, updates),
32953
+ graph: _extends({}, this.props.graph, updates)
32657
32954
  });
32658
32955
  },
32659
32956
  style: styles$a.singleSelectShort
@@ -32669,13 +32966,16 @@ class InteractiveGraphEditor extends React.Component {
32669
32966
  ,
32670
32967
  placeholder: "",
32671
32968
  onChange: newValue => {
32672
- const graph = _extends({}, this.props.correct, {
32969
+ var _this$props$graph5, _this$props$graph6;
32970
+ invariant(this.props.correct.type === "polygon", `Expected correct answer type to be polygon, but got ${this.props.correct.type}`);
32971
+ 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}`);
32972
+ const updates = {
32673
32973
  snapTo: newValue,
32674
32974
  coords: null
32675
- });
32975
+ };
32676
32976
  this.props.onChange({
32677
- correct: graph,
32678
- graph: graph
32977
+ correct: _extends({}, this.props.correct, updates),
32978
+ graph: _extends({}, this.props.graph, updates)
32679
32979
  });
32680
32980
  },
32681
32981
  style: styles$a.singleSelectShort
@@ -32696,8 +32996,9 @@ class InteractiveGraphEditor extends React.Component {
32696
32996
  // Don't show indeterminate checkbox state
32697
32997
  !!((_this$props$correct8 = this.props.correct) != null && _this$props$correct8.showAngles),
32698
32998
  onChange: () => {
32699
- var _this$props$graph4;
32700
- if (((_this$props$graph4 = this.props.graph) == null ? void 0 : _this$props$graph4.type) === "polygon") {
32999
+ var _this$props$graph7;
33000
+ if (((_this$props$graph7 = this.props.graph) == null ? void 0 : _this$props$graph7.type) === "polygon") {
33001
+ invariant(this.props.correct.type === "polygon", `Expected graph type to be polygon, but got ${this.props.correct.type}`);
32701
33002
  this.props.onChange({
32702
33003
  correct: _extends({}, this.props.correct, {
32703
33004
  showAngles: !this.props.correct.showAngles
@@ -32716,8 +33017,8 @@ class InteractiveGraphEditor extends React.Component {
32716
33017
  // Don't show indeterminate checkbox state
32717
33018
  !!((_this$props$correct9 = this.props.correct) != null && _this$props$correct9.showSides),
32718
33019
  onChange: () => {
32719
- var _this$props$graph5;
32720
- if (((_this$props$graph5 = this.props.graph) == null ? void 0 : _this$props$graph5.type) === "polygon") {
33020
+ var _this$props$graph8;
33021
+ if (((_this$props$graph8 = this.props.graph) == null ? void 0 : _this$props$graph8.type) === "polygon" && this.props.correct.type === "polygon") {
32721
33022
  this.props.onChange({
32722
33023
  correct: _extends({}, this.props.correct, {
32723
33024
  showSides: !this.props.correct.showSides
@@ -32745,7 +33046,7 @@ class InteractiveGraphEditor extends React.Component {
32745
33046
  }
32746
33047
  });
32747
33048
  }
32748
- })), ((_this$props$graph6 = this.props.graph) == null ? void 0 : _this$props$graph6.type) &&
33049
+ })), ((_this$props$graph9 = this.props.graph) == null ? void 0 : _this$props$graph9.type) &&
32749
33050
  // TODO(LEMS-2228): Remove flags once this is fully released
32750
33051
  shouldShowStartCoordsUI(this.props.apiOptions.flags, this.props.graph) && /*#__PURE__*/React.createElement(StartCoordsSettings, _extends({}, this.props.graph, {
32751
33052
  range: this.props.range,
@@ -32768,7 +33069,22 @@ class InteractiveGraphEditor extends React.Component {
32768
33069
  label: "Student answer must"
32769
33070
  }, /*#__PURE__*/React.createElement(SingleSelect, {
32770
33071
  selectedValue: this.props.correct.match || "exact",
32771
- onChange: this.changeMatchType
33072
+ onChange: newValue => {
33073
+ invariant(this.props.correct.type === "polygon", `Expected graph type to be polygon, but got ${this.props.correct.type}`);
33074
+ const correct = _extends({}, this.props.correct, {
33075
+ // TODO(benchristel): this cast is necessary
33076
+ // because "exact" is not actually a valid
33077
+ // value for `match`; a value of undefined
33078
+ // means exact matching. The code happens
33079
+ // to work because "exact" falls through
33080
+ // to the correct else branch in
33081
+ // InteractiveGraph.validate()
33082
+ match: newValue
33083
+ });
33084
+ this.props.onChange({
33085
+ correct
33086
+ });
33087
+ }
32772
33088
  // Never uses placeholder, always has value
32773
33089
  ,
32774
33090
  placeholder: "",
@@ -32789,7 +33105,20 @@ class InteractiveGraphEditor extends React.Component {
32789
33105
  label: "Student answer must"
32790
33106
  }, /*#__PURE__*/React.createElement(SingleSelect, {
32791
33107
  selectedValue: this.props.correct.match || "exact",
32792
- onChange: this.changeMatchType
33108
+ onChange: newValue => {
33109
+ this.props.onChange({
33110
+ correct: _extends({}, this.props.correct, {
33111
+ // TODO(benchristel): this cast is necessary
33112
+ // because "exact" is not actually a valid
33113
+ // value for `match`; a value of undefined
33114
+ // means exact matching. The code happens
33115
+ // to work because "exact" falls through
33116
+ // to the correct else branch in
33117
+ // InteractiveGraph.validate()
33118
+ match: newValue
33119
+ })
33120
+ });
33121
+ }
32793
33122
  // Never uses placeholder, always has value
32794
33123
  ,
32795
33124
  placeholder: "",
@@ -32803,14 +33132,18 @@ class InteractiveGraphEditor extends React.Component {
32803
33132
  })), /*#__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."))),
32804
33133
  // Only show the "Add locked figure" dropdown if the graph
32805
33134
  // is using Mafs.
32806
- 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, {
32807
- 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"],
32808
- 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"],
33135
+ 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, {
33136
+ showM2bFeatures: (_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-m2b"],
33137
+ showLabelsFlag: (_this$props$apiOption4 = this.props.apiOptions) == null || (_this$props$apiOption4 = _this$props$apiOption4.flags) == null || (_this$props$apiOption4 = _this$props$apiOption4.mafs) == null ? void 0 : _this$props$apiOption4["interactive-graph-locked-features-labels"],
32809
33138
  figures: this.props.lockedFigures,
32810
33139
  onChange: this.props.onChange
32811
33140
  }));
32812
33141
  }
32813
33142
  }
33143
+
33144
+ // Merges two graphs that have the same `type`. Properties defined in `b`
33145
+ // overwrite properties of the same name in `a`. Throws an exception if the
33146
+ // types are different or not recognized.
32814
33147
  InteractiveGraphEditor.widgetName = "interactive-graph";
32815
33148
  InteractiveGraphEditor.defaultProps = _extends({}, InteractiveGraph.defaultProps, {
32816
33149
  valid: true,
@@ -32821,6 +33154,45 @@ InteractiveGraphEditor.defaultProps = _extends({}, InteractiveGraph.defaultProps
32821
33154
  coords: null
32822
33155
  }
32823
33156
  });
33157
+ function mergeGraphs(a, b) {
33158
+ if (a.type !== b.type) {
33159
+ throw new Error(`Cannot merge graphs with different types (${a.type} and ${b.type})`);
33160
+ }
33161
+ switch (a.type) {
33162
+ case "angle":
33163
+ invariant(b.type === "angle");
33164
+ return _extends({}, a, b);
33165
+ case "circle":
33166
+ invariant(b.type === "circle");
33167
+ return _extends({}, a, b);
33168
+ case "linear":
33169
+ invariant(b.type === "linear");
33170
+ return _extends({}, a, b);
33171
+ case "linear-system":
33172
+ invariant(b.type === "linear-system");
33173
+ return _extends({}, a, b);
33174
+ case "point":
33175
+ invariant(b.type === "point");
33176
+ return _extends({}, a, b);
33177
+ case "polygon":
33178
+ invariant(b.type === "polygon");
33179
+ return _extends({}, a, b);
33180
+ case "quadratic":
33181
+ invariant(b.type === "quadratic");
33182
+ return _extends({}, a, b);
33183
+ case "ray":
33184
+ invariant(b.type === "ray");
33185
+ return _extends({}, a, b);
33186
+ case "segment":
33187
+ invariant(b.type === "segment");
33188
+ return _extends({}, a, b);
33189
+ case "sinusoid":
33190
+ invariant(b.type === "sinusoid");
33191
+ return _extends({}, a, b);
33192
+ default:
33193
+ throw new UnreachableCaseError(a);
33194
+ }
33195
+ }
32824
33196
  const styles$a = StyleSheet.create({
32825
33197
  singleSelectShort: {
32826
33198
  // Non-standard spacing, but it's the smallest we can go