@khanacademy/perseus-editor 6.3.0 → 6.5.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/index.js CHANGED
@@ -25,10 +25,10 @@ var wonderBlocksDropdown = require('@khanacademy/wonder-blocks-dropdown');
25
25
  var wonderBlocksForm = require('@khanacademy/wonder-blocks-form');
26
26
  var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
27
27
  var Banner = require('@khanacademy/wonder-blocks-banner');
28
- var wonderBlocksAccordion = require('@khanacademy/wonder-blocks-accordion');
29
28
  var Button = require('@khanacademy/wonder-blocks-button');
30
- var trashIcon = require('@phosphor-icons/core/bold/trash-bold.svg');
31
29
  var Switch = require('@khanacademy/wonder-blocks-switch');
30
+ var wonderBlocksAccordion = require('@khanacademy/wonder-blocks-accordion');
31
+ var trashIcon = require('@phosphor-icons/core/bold/trash-bold.svg');
32
32
 
33
33
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
34
34
 
@@ -62,12 +62,12 @@ var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
62
62
  var KAS__namespace = /*#__PURE__*/_interopNamespace(KAS);
63
63
  var Banner__default = /*#__PURE__*/_interopDefaultLegacy(Banner);
64
64
  var Button__default = /*#__PURE__*/_interopDefaultLegacy(Button);
65
- var trashIcon__default = /*#__PURE__*/_interopDefaultLegacy(trashIcon);
66
65
  var Switch__default = /*#__PURE__*/_interopDefaultLegacy(Switch);
66
+ var trashIcon__default = /*#__PURE__*/_interopDefaultLegacy(trashIcon);
67
67
 
68
68
  // This file is processed by a Rollup plugin (replace) to inject the production
69
69
  const libName = "@khanacademy/perseus-editor";
70
- const libVersion = "6.3.0";
70
+ const libVersion = "6.5.0";
71
71
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
72
72
 
73
73
  function _extends() {
@@ -262,13 +262,13 @@ class SectionControlButton extends React__namespace.Component {
262
262
  role: "button",
263
263
  hideDefaultFocusRing: true,
264
264
  "aria-label": title,
265
- style: styles$v.button
265
+ style: styles$z.button
266
266
  }, _ref => {
267
267
  return /*#__PURE__*/React__namespace.createElement(InlineIcon$b, icon);
268
268
  });
269
269
  }
270
270
  }
271
- const styles$v = aphrodite.StyleSheet.create({
271
+ const styles$z = aphrodite.StyleSheet.create({
272
272
  button: {
273
273
  backgroundColor: wonderBlocksTokens.color.gold,
274
274
  margin: 2,
@@ -20768,6 +20768,27 @@ class WidgetSelect extends React__namespace.Component {
20768
20768
  }
20769
20769
  }
20770
20770
 
20771
+ /**
20772
+ * Displays a caret in either expanded or collapsed state. For use in headings
20773
+ * that collapse a region.
20774
+ */
20775
+ function ToggleableCaret(props) {
20776
+ const iconStyle = props.isExpanded ? styles$y.expanded : styles$y.collapsed;
20777
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksIcon.PhosphorIcon, {
20778
+ icon: caretRight__default["default"],
20779
+ style: iconStyle
20780
+ });
20781
+ }
20782
+ const styles$y = aphrodite.StyleSheet.create({
20783
+ collapsed: {
20784
+ transition: ".15s"
20785
+ },
20786
+ expanded: {
20787
+ transform: "rotate(90deg)",
20788
+ transition: ".15s"
20789
+ }
20790
+ });
20791
+
20771
20792
  /**
20772
20793
  * Displays a collapsable list of TeX rendering errors.
20773
20794
  */
@@ -20787,19 +20808,17 @@ class TexErrorView extends React__namespace.Component {
20787
20808
  const {
20788
20809
  showErrors
20789
20810
  } = this.state;
20790
- const iconStyle = showErrors ? styles$u.iconExpanded : styles$u.iconCollapsed;
20791
20811
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
20792
- style: styles$u.errorContainer
20812
+ style: styles$x.errorContainer
20793
20813
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
20794
- style: styles$u.title,
20814
+ style: styles$x.title,
20795
20815
  onClick: this.handleToggleTexErrors
20796
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksIcon.PhosphorIcon, {
20797
- icon: caretRight__default["default"],
20798
- style: iconStyle
20816
+ }, /*#__PURE__*/React__namespace.createElement(ToggleableCaret, {
20817
+ isExpanded: showErrors
20799
20818
  }), "\xA0 TeX Errors (", errorList.length, ")"), showErrors && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
20800
- style: styles$u.errorExplanation
20819
+ style: styles$x.errorExplanation
20801
20820
  }, "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__namespace.createElement(wonderBlocksCore.View, {
20802
- style: styles$u.error,
20821
+ style: styles$x.error,
20803
20822
  key: index
20804
20823
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
20805
20824
  style: {
@@ -20808,7 +20827,7 @@ class TexErrorView extends React__namespace.Component {
20808
20827
  }, e.math), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, e.message))));
20809
20828
  }
20810
20829
  }
20811
- const styles$u = aphrodite.StyleSheet.create({
20830
+ const styles$x = aphrodite.StyleSheet.create({
20812
20831
  title: {
20813
20832
  backgroundColor: "#eee",
20814
20833
  fontSize: "1.25em",
@@ -20828,13 +20847,6 @@ const styles$u = aphrodite.StyleSheet.create({
20828
20847
  },
20829
20848
  error: {
20830
20849
  padding: "4px 10px"
20831
- },
20832
- iconCollapsed: {
20833
- transition: ".15s"
20834
- },
20835
- iconExpanded: {
20836
- transform: "rotate(90deg)",
20837
- transition: ".15s"
20838
20850
  }
20839
20851
  });
20840
20852
 
@@ -24565,12 +24577,12 @@ class SimpleButton extends React__namespace.Component {
24565
24577
  ...otherProps
24566
24578
  } = this.props;
24567
24579
  return /*#__PURE__*/React__namespace.createElement("div", _extends({
24568
- className: aphrodite.css(styles$t.baseButton, color === "green" && styles$t.green, color === "orange" && styles$t.orange),
24580
+ className: aphrodite.css(styles$w.baseButton, color === "green" && styles$w.green, color === "orange" && styles$w.orange),
24569
24581
  onClick: onClick
24570
24582
  }, otherProps), children);
24571
24583
  }
24572
24584
  }
24573
- const styles$t = aphrodite.StyleSheet.create({
24585
+ const styles$w = aphrodite.StyleSheet.create({
24574
24586
  baseButton: {
24575
24587
  top: "0",
24576
24588
  fontSize: "11px",
@@ -24783,23 +24795,23 @@ const LeafContainer = _ref2 => {
24783
24795
  } = _ref2;
24784
24796
  const hasPreviewHeading = shape.type === "content" || shape.type === "hint";
24785
24797
  const previewHeading = hasPreviewHeading && /*#__PURE__*/React__namespace.createElement("div", {
24786
- className: aphrodite.css(styles$s.containerHeader)
24798
+ className: aphrodite.css(styles$v.containerHeader)
24787
24799
  }, /*#__PURE__*/React__namespace.createElement(Header, {
24788
24800
  depth: path.length,
24789
- className: aphrodite.css(styles$s.containerTitle)
24801
+ className: aphrodite.css(styles$v.containerTitle)
24790
24802
  }, capitalize(name)));
24791
24803
  return /*#__PURE__*/React__namespace.createElement("div", {
24792
- className: aphrodite.css(styles$s.container)
24804
+ className: aphrodite.css(styles$v.container)
24793
24805
  }, /*#__PURE__*/React__namespace.createElement("span", {
24794
- className: aphrodite.css(styles$s.row, styles$s.rowHeading)
24806
+ className: aphrodite.css(styles$v.row, styles$v.rowHeading)
24795
24807
  }, /*#__PURE__*/React__namespace.createElement("div", {
24796
- className: aphrodite.css(styles$s.columnLeft)
24808
+ className: aphrodite.css(styles$v.columnLeft)
24797
24809
  }, /*#__PURE__*/React__namespace.createElement("div", {
24798
- className: "pod-title " + aphrodite.css(styles$s.containerHeader)
24810
+ className: "pod-title " + aphrodite.css(styles$v.containerHeader)
24799
24811
  }, /*#__PURE__*/React__namespace.createElement("div", {
24800
- className: aphrodite.css(styles$s.containerTitle)
24812
+ className: aphrodite.css(styles$v.containerTitle)
24801
24813
  }, capitalize(name)), controls)), /*#__PURE__*/React__namespace.createElement("div", {
24802
- className: aphrodite.css(styles$s.columnRight)
24814
+ className: aphrodite.css(styles$v.columnRight)
24803
24815
  }, previewHeading)), children);
24804
24816
  };
24805
24817
  const ArrayContainer = props => {
@@ -24812,11 +24824,11 @@ const ArrayContainer = props => {
24812
24824
  actions
24813
24825
  } = props;
24814
24826
  return /*#__PURE__*/React__namespace.createElement("div", {
24815
- className: aphrodite.css(styles$s.container)
24827
+ className: aphrodite.css(styles$v.container)
24816
24828
  }, controls && /*#__PURE__*/React__namespace.createElement("div", {
24817
- className: aphrodite.css(styles$s.columnLeft, styles$s.containerHeader)
24829
+ className: aphrodite.css(styles$v.columnLeft, styles$v.containerHeader)
24818
24830
  }, controls), /*#__PURE__*/React__namespace.createElement("div", null, children), /*#__PURE__*/React__namespace.createElement("div", {
24819
- className: aphrodite.css(styles$s.columnLeft)
24831
+ className: aphrodite.css(styles$v.columnLeft)
24820
24832
  }, /*#__PURE__*/React__namespace.createElement("a", {
24821
24833
  href: "javascript:void 0",
24822
24834
  onClick: () => actions.addArrayElement(path, shape.elementShape)
@@ -24830,28 +24842,28 @@ const ObjectContainer = _ref3 => {
24830
24842
  path
24831
24843
  } = _ref3;
24832
24844
  const headingEditor = /*#__PURE__*/React__namespace.createElement("div", {
24833
- className: aphrodite.css(styles$s.containerHeader)
24845
+ className: aphrodite.css(styles$v.containerHeader)
24834
24846
  }, /*#__PURE__*/React__namespace.createElement(Header, {
24835
24847
  depth: path.length,
24836
- className: aphrodite.css(styles$s.containerTitle)
24848
+ className: aphrodite.css(styles$v.containerTitle)
24837
24849
  }, capitalize(name)), controls);
24838
24850
  const headingPreview = (name || controls) && /*#__PURE__*/React__namespace.createElement("div", {
24839
- className: aphrodite.css(styles$s.containerHeader, styles$s.previewCollectionHeader)
24851
+ className: aphrodite.css(styles$v.containerHeader, styles$v.previewCollectionHeader)
24840
24852
  }, /*#__PURE__*/React__namespace.createElement(Header, {
24841
24853
  depth: path.length,
24842
- className: aphrodite.css(styles$s.containerTitle)
24854
+ className: aphrodite.css(styles$v.containerTitle)
24843
24855
  }, capitalize(name)));
24844
24856
  const hasBothHeadings = headingEditor && headingPreview;
24845
24857
  return /*#__PURE__*/React__namespace.createElement("div", {
24846
- className: aphrodite.css(styles$s.container)
24858
+ className: aphrodite.css(styles$v.container)
24847
24859
  }, hasBothHeadings && /*#__PURE__*/React__namespace.createElement("span", {
24848
- className: aphrodite.css(styles$s.row)
24860
+ className: aphrodite.css(styles$v.row)
24849
24861
  }, /*#__PURE__*/React__namespace.createElement("div", {
24850
- className: aphrodite.css(styles$s.columnLeft)
24862
+ className: aphrodite.css(styles$v.columnLeft)
24851
24863
  }, headingEditor), /*#__PURE__*/React__namespace.createElement("div", {
24852
- className: aphrodite.css(styles$s.columnRight)
24864
+ className: aphrodite.css(styles$v.columnRight)
24853
24865
  }, headingPreview)), /*#__PURE__*/React__namespace.createElement("div", {
24854
- className: aphrodite.css(path.length > 0 && styles$s.contentIndent)
24866
+ className: aphrodite.css(path.length > 0 && styles$v.contentIndent)
24855
24867
  }, children));
24856
24868
  };
24857
24869
 
@@ -24971,11 +24983,11 @@ const ItemNodeContent = withStickiness(props => {
24971
24983
  className: "framework-perseus"
24972
24984
  }, lens(renderers).get(path));
24973
24985
  return /*#__PURE__*/React__namespace.createElement("span", null, /*#__PURE__*/React__namespace.createElement("div", {
24974
- className: aphrodite.css(styles$s.row)
24986
+ className: aphrodite.css(styles$v.row)
24975
24987
  }, /*#__PURE__*/React__namespace.createElement("div", {
24976
- className: aphrodite.css(styles$s.columnLeft)
24988
+ className: aphrodite.css(styles$v.columnLeft)
24977
24989
  }, /*#__PURE__*/React__namespace.createElement("div", {
24978
- className: aphrodite.css(sticky && styles$s.sticky)
24990
+ className: aphrodite.css(sticky && styles$v.sticky)
24979
24991
  },
24980
24992
  /*#__PURE__*/
24981
24993
  // @ts-expect-error - TS2769 - No overload matches this call.
@@ -24983,9 +24995,9 @@ const ItemNodeContent = withStickiness(props => {
24983
24995
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
24984
24996
  apiOptions: apiOptions
24985
24997
  })))), /*#__PURE__*/React__namespace.createElement("div", {
24986
- className: aphrodite.css(styles$s.columnRight)
24998
+ className: aphrodite.css(styles$v.columnRight)
24987
24999
  }, /*#__PURE__*/React__namespace.createElement("div", {
24988
- className: aphrodite.css(sticky && styles$s.sticky)
25000
+ className: aphrodite.css(sticky && styles$v.sticky)
24989
25001
  }, preview))));
24990
25002
  });
24991
25003
  const HintNodeContent = withStickiness(props => {
@@ -25001,13 +25013,13 @@ const HintNodeContent = withStickiness(props => {
25001
25013
  className: "framework-perseus"
25002
25014
  }, lens(renderers).get(path));
25003
25015
  return /*#__PURE__*/React__namespace.createElement("div", {
25004
- className: aphrodite.css(styles$s.row)
25016
+ className: aphrodite.css(styles$v.row)
25005
25017
  }, /*#__PURE__*/React__namespace.createElement("div", {
25006
- className: aphrodite.css(styles$s.columnLeft)
25018
+ className: aphrodite.css(styles$v.columnLeft)
25007
25019
  }, /*#__PURE__*/React__namespace.createElement("div", {
25008
- className: aphrodite.css(sticky && styles$s.sticky)
25020
+ className: aphrodite.css(sticky && styles$v.sticky)
25009
25021
  }, /*#__PURE__*/React__namespace.createElement(HintEditor, _extends({}, data, {
25010
- className: aphrodite.css(styles$s.hintEditor),
25022
+ className: aphrodite.css(styles$v.hintEditor),
25011
25023
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
25012
25024
  apiOptions: apiOptions,
25013
25025
  showTitle: false,
@@ -25022,9 +25034,9 @@ const HintNodeContent = withStickiness(props => {
25022
25034
  isFirst: true,
25023
25035
  isLast: true
25024
25036
  })))), /*#__PURE__*/React__namespace.createElement("div", {
25025
- className: aphrodite.css(styles$s.columnRight)
25037
+ className: aphrodite.css(styles$v.columnRight)
25026
25038
  }, /*#__PURE__*/React__namespace.createElement("div", {
25027
- className: aphrodite.css(sticky && styles$s.sticky)
25039
+ className: aphrodite.css(sticky && styles$v.sticky)
25028
25040
  }, preview)));
25029
25041
  });
25030
25042
  const TagsNodeContent = props => {
@@ -25041,9 +25053,9 @@ const TagsNodeContent = props => {
25041
25053
  return null;
25042
25054
  }
25043
25055
  return /*#__PURE__*/React__namespace.createElement("div", {
25044
- className: aphrodite.css(styles$s.columnLeft)
25056
+ className: aphrodite.css(styles$v.columnLeft)
25045
25057
  }, /*#__PURE__*/React__namespace.createElement("div", {
25046
- className: aphrodite.css(styles$s.tagsEditor)
25058
+ className: aphrodite.css(styles$v.tagsEditor)
25047
25059
  }, /*#__PURE__*/React__namespace.createElement(GroupMetadataEditor, {
25048
25060
  value: data,
25049
25061
  onChange: newVal => actions.setValueAtPath(path, newVal),
@@ -25074,23 +25086,23 @@ const ArrayNodeContent = props => {
25074
25086
  const subpath = path.concat(i);
25075
25087
  const controls = [i > 0 && /*#__PURE__*/React__namespace.createElement("div", {
25076
25088
  key: "moveArrayElementUp",
25077
- className: aphrodite.css(styles$s.control)
25089
+ className: aphrodite.css(styles$v.control)
25078
25090
  }, /*#__PURE__*/React__namespace.createElement(SimpleButton, {
25079
25091
  color: "orange",
25080
25092
  title: "Move up",
25081
25093
  onClick: () => actions.moveArrayElementUp(subpath)
25082
25094
  }, /*#__PURE__*/React__namespace.createElement("div", {
25083
- className: aphrodite.css(styles$s.verticalFlip)
25095
+ className: aphrodite.css(styles$v.verticalFlip)
25084
25096
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon$6, iconChevronDown)))), i < data.length - 1 && /*#__PURE__*/React__namespace.createElement("div", {
25085
25097
  key: "moveArrayElementDown",
25086
- className: aphrodite.css(styles$s.control)
25098
+ className: aphrodite.css(styles$v.control)
25087
25099
  }, /*#__PURE__*/React__namespace.createElement(SimpleButton, {
25088
25100
  color: "orange",
25089
25101
  title: "Move down",
25090
25102
  onClick: () => actions.moveArrayElementDown(subpath)
25091
25103
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon$6, iconChevronDown))), /*#__PURE__*/React__namespace.createElement("div", {
25092
25104
  key: "removeArrayElement",
25093
- className: aphrodite.css(styles$s.control)
25105
+ className: aphrodite.css(styles$v.control)
25094
25106
  }, /*#__PURE__*/React__namespace.createElement(SimpleButton, {
25095
25107
  color: "orange",
25096
25108
  title: "Delete",
@@ -25098,7 +25110,7 @@ const ArrayNodeContent = props => {
25098
25110
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon$6, iconTrash$4)))];
25099
25111
  return /*#__PURE__*/React__namespace.createElement("div", {
25100
25112
  key: i,
25101
- className: aphrodite.css(styles$s.arrayElement, !elementIsLeaf && styles$s.arrayElementAndNotLeaf)
25113
+ className: aphrodite.css(styles$v.arrayElement, !elementIsLeaf && styles$v.arrayElementAndNotLeaf)
25102
25114
  }, /*#__PURE__*/React__namespace.createElement(NodeContainer, _extends({}, otherProps, {
25103
25115
  key: i,
25104
25116
  shape: shape.elementShape,
@@ -25124,7 +25136,7 @@ const ObjectNodeContent = props => {
25124
25136
  // made sense to the shape's author is the order in which we'll iterate :)
25125
25137
  const children = Object.keys(shape.shape).map(subkey => /*#__PURE__*/React__namespace.createElement("div", {
25126
25138
  key: subkey,
25127
- className: aphrodite.css(styles$s.objectElement)
25139
+ className: aphrodite.css(styles$v.objectElement)
25128
25140
  }, /*#__PURE__*/React__namespace.createElement(NodeContainer, _extends({}, otherProps, {
25129
25141
  shape: shape.shape[subkey],
25130
25142
  data: data[subkey],
@@ -25294,7 +25306,7 @@ class MultiRendererEditor extends React__namespace.Component {
25294
25306
  }, this._renderContent());
25295
25307
  }
25296
25308
  }
25297
- const styles$s = aphrodite.StyleSheet.create({
25309
+ const styles$v = aphrodite.StyleSheet.create({
25298
25310
  // This is used in a number of places throughout this file.
25299
25311
  container: {},
25300
25312
  // eslint-disable-next-line react-native/no-unused-styles
@@ -25446,47 +25458,47 @@ const StatefulArticleEditor = createReactClass__default["default"]({
25446
25458
  screen
25447
25459
  } = this.state;
25448
25460
  return /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("div", {
25449
- style: styles$r.controlBar
25461
+ style: styles$u.controlBar
25450
25462
  }, /*#__PURE__*/React__namespace.createElement("span", {
25451
- style: styles$r.controls
25463
+ style: styles$u.controls
25452
25464
  }, "Mode:", " ", /*#__PURE__*/React__namespace.createElement("span", {
25453
25465
  onClick: () => this.setState({
25454
25466
  mode: "edit"
25455
25467
  }),
25456
- style: mode === "edit" ? styles$r.controlSelected : styles$r.control
25468
+ style: mode === "edit" ? styles$u.controlSelected : styles$u.control
25457
25469
  }, "EDIT"), " | ", /*#__PURE__*/React__namespace.createElement("span", {
25458
25470
  onClick: () => this.setState({
25459
25471
  mode: "preview"
25460
25472
  }),
25461
- style: mode === "preview" ? styles$r.controlSelected : styles$r.control
25473
+ style: mode === "preview" ? styles$u.controlSelected : styles$u.control
25462
25474
  }, "PREVIEW"), " | ", /*#__PURE__*/React__namespace.createElement("span", {
25463
25475
  onClick: () => this.setState({
25464
25476
  mode: "json"
25465
25477
  }),
25466
- style: mode === "json" ? styles$r.controlSelected : styles$r.control
25478
+ style: mode === "json" ? styles$u.controlSelected : styles$u.control
25467
25479
  }, "JSON")), /*#__PURE__*/React__namespace.createElement("span", {
25468
- style: styles$r.controls
25480
+ style: styles$u.controls
25469
25481
  }, "Screen:", " ", /*#__PURE__*/React__namespace.createElement("span", {
25470
25482
  onClick: () => this.setState({
25471
25483
  screen: "phone"
25472
25484
  }),
25473
- style: screen === "phone" ? styles$r.controlSelected : styles$r.control
25485
+ style: screen === "phone" ? styles$u.controlSelected : styles$u.control
25474
25486
  }, "PHONE"), " | ", /*#__PURE__*/React__namespace.createElement("span", {
25475
25487
  onClick: () => this.setState({
25476
25488
  screen: "tablet"
25477
25489
  }),
25478
- style: screen === "tablet" ? styles$r.controlSelected : styles$r.control
25490
+ style: screen === "tablet" ? styles$u.controlSelected : styles$u.control
25479
25491
  }, "TABLET"), " | ", /*#__PURE__*/React__namespace.createElement("span", {
25480
25492
  onClick: () => this.setState({
25481
25493
  screen: "desktop"
25482
25494
  }),
25483
- style: screen === "desktop" ? styles$r.controlSelected : styles$r.control
25495
+ style: screen === "desktop" ? styles$u.controlSelected : styles$u.control
25484
25496
  }, "DESKTOP"))), /*#__PURE__*/React__namespace.createElement("div", {
25485
- style: styles$r.editor
25497
+ style: styles$u.editor
25486
25498
  }, /*#__PURE__*/React__namespace.createElement(ArticleEditor, this.state)));
25487
25499
  }
25488
25500
  });
25489
- const styles$r = {
25501
+ const styles$u = {
25490
25502
  controlBar: {
25491
25503
  marginBottom: 25,
25492
25504
  marginTop: 5
@@ -27025,10 +27037,10 @@ class GradedGroupEditor extends React__namespace.Component {
27025
27037
  }, /*#__PURE__*/React__namespace.createElement("div", {
27026
27038
  className: "perseus-widget-row"
27027
27039
  }, /*#__PURE__*/React__namespace.createElement("label", {
27028
- className: aphrodite.css(styles$q.title)
27040
+ className: aphrodite.css(styles$t.title)
27029
27041
  }, "Title:", " ", /*#__PURE__*/React__namespace.createElement(TextInput$6, {
27030
27042
  value: this.props.title,
27031
- className: aphrodite.css(styles$q.input)
27043
+ className: aphrodite.css(styles$t.input)
27032
27044
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
27033
27045
  ,
27034
27046
  onChange: this.change("title")
@@ -27053,7 +27065,7 @@ class GradedGroupEditor extends React__namespace.Component {
27053
27065
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon$4, iconPlus$1), " Add a hint"), this.props.hint && /*#__PURE__*/React__namespace.createElement("div", {
27054
27066
  className: "perseus-hint-editor"
27055
27067
  }, /*#__PURE__*/React__namespace.createElement("div", {
27056
- className: aphrodite.css(styles$q.hintsTitle)
27068
+ className: aphrodite.css(styles$t.hintsTitle)
27057
27069
  }, "Hint"), /*#__PURE__*/React__namespace.createElement(Editor, {
27058
27070
  ref: this.hintEditor,
27059
27071
  content: this.props.hint ? this.props.hint.content : "",
@@ -27085,7 +27097,7 @@ class GradedGroupEditor extends React__namespace.Component {
27085
27097
  };
27086
27098
  };
27087
27099
  }
27088
- const styles$q = aphrodite.StyleSheet.create({
27100
+ const styles$t = aphrodite.StyleSheet.create({
27089
27101
  title: {
27090
27102
  fontSize: 18,
27091
27103
  fontWeight: "bold"
@@ -28456,13 +28468,13 @@ function MathquillInput(props) {
28456
28468
  }
28457
28469
  });
28458
28470
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
28459
- style: styles$p.outerWrapper
28471
+ style: styles$s.outerWrapper
28460
28472
  }, /*#__PURE__*/React__namespace.createElement("span", {
28461
28473
  ref: mathFieldWrapperRef,
28462
28474
  className: "perseus-math-input mq-editable-field mq-math-mode"
28463
28475
  }));
28464
28476
  }
28465
- const styles$p = aphrodite.StyleSheet.create({
28477
+ const styles$s = aphrodite.StyleSheet.create({
28466
28478
  outerWrapper: {
28467
28479
  display: "inline-block",
28468
28480
  borderStyle: "solid",
@@ -29548,7 +29560,7 @@ const GraphPointsCountSelector = _ref => {
29548
29560
  // Never uses placeholder, always has value
29549
29561
  ,
29550
29562
  placeholder: "",
29551
- style: styles$o.singleSelectShort
29563
+ style: styles$r.singleSelectShort
29552
29564
  }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29553
29565
  key: n,
29554
29566
  value: `${n}`,
@@ -29559,7 +29571,7 @@ const GraphPointsCountSelector = _ref => {
29559
29571
  label: "unlimited"
29560
29572
  })]);
29561
29573
  };
29562
- const styles$o = aphrodite.StyleSheet.create({
29574
+ const styles$r = aphrodite.StyleSheet.create({
29563
29575
  singleSelectShort: {
29564
29576
  // Non-standard spacing, but it's the smallest we can go
29565
29577
  // without running into styling issues with the dropdown.
@@ -29572,7 +29584,7 @@ const GraphTypeSelector = props => {
29572
29584
  selectedValue: props.graphType,
29573
29585
  onChange: props.onChange,
29574
29586
  placeholder: "Select a graph type",
29575
- style: styles$n.singleSelectShort
29587
+ style: styles$q.singleSelectShort
29576
29588
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29577
29589
  value: "linear",
29578
29590
  label: "Linear function"
@@ -29605,7 +29617,7 @@ const GraphTypeSelector = props => {
29605
29617
  label: "Angle"
29606
29618
  }));
29607
29619
  };
29608
- const styles$n = aphrodite.StyleSheet.create({
29620
+ const styles$q = aphrodite.StyleSheet.create({
29609
29621
  singleSelectShort: {
29610
29622
  // Non-standard spacing, but it's the smallest we can go
29611
29623
  // without running into styling issues with the dropdown.
@@ -29622,16 +29634,16 @@ const LabeledRow = props => {
29622
29634
  style
29623
29635
  } = props;
29624
29636
  return /*#__PURE__*/React__namespace.createElement("label", {
29625
- className: aphrodite.css(styles$m.label)
29637
+ className: aphrodite.css(styles$p.label)
29626
29638
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
29627
- style: [styles$m.row, style]
29639
+ style: [styles$p.row, style]
29628
29640
  }, labelSide === "start" || /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, {
29629
- style: styles$m.spaceEnd
29641
+ style: styles$p.spaceEnd
29630
29642
  }, label), children, labelSide === "end" && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, {
29631
- style: styles$m.spaceStart
29643
+ style: styles$p.spaceStart
29632
29644
  }, label)));
29633
29645
  };
29634
- const styles$m = aphrodite.StyleSheet.create({
29646
+ const styles$p = aphrodite.StyleSheet.create({
29635
29647
  label: {
29636
29648
  width: "fit-content"
29637
29649
  },
@@ -29664,6 +29676,31 @@ const defaultBackgroundImage$1 = {
29664
29676
  function numSteps(range, step) {
29665
29677
  return Math.floor((range[1] - range[0]) / step);
29666
29678
  }
29679
+ function Heading(_ref) {
29680
+ let {
29681
+ title,
29682
+ isOpen,
29683
+ onToggle
29684
+ } = _ref;
29685
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
29686
+ style: {
29687
+ flexDirection: "row",
29688
+ justifyContent: "space-between",
29689
+ backgroundColor: wonderBlocksTokens.color.offBlack8,
29690
+ padding: wonderBlocksTokens.spacing.xSmall_8,
29691
+ marginTop: wonderBlocksTokens.spacing.small_12,
29692
+ // NOTE(jeremy): This is the inverse of the @editorPadding CSS
29693
+ // variable found in perseus-editor.less. For now, it must
29694
+ // match otherwise there's a gap from this header to the edge
29695
+ // of the editor borders.
29696
+ marginInline: -10,
29697
+ cursor: "pointer"
29698
+ },
29699
+ onClick: () => onToggle?.(!isOpen)
29700
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, null, title), /*#__PURE__*/React__namespace.createElement(ToggleableCaret, {
29701
+ isExpanded: isOpen
29702
+ }));
29703
+ }
29667
29704
  class InteractiveGraphSettings extends React__namespace.Component {
29668
29705
  _isMounted = false;
29669
29706
  bgUrlRef = /*#__PURE__*/React__namespace.createRef();
@@ -29684,7 +29721,10 @@ class InteractiveGraphSettings extends React__namespace.Component {
29684
29721
  }
29685
29722
  constructor(props) {
29686
29723
  super(props);
29687
- this.state = InteractiveGraphSettings.stateFromProps(props);
29724
+ this.state = {
29725
+ isExpanded: true,
29726
+ ...InteractiveGraphSettings.stateFromProps(props)
29727
+ };
29688
29728
  }
29689
29729
  static defaultProps = {
29690
29730
  box: [perseus.interactiveSizes.defaultBoxSizeSmall, perseus.interactiveSizes.defaultBoxSizeSmall],
@@ -29962,7 +30002,13 @@ class InteractiveGraphSettings extends React__namespace.Component {
29962
30002
  };
29963
30003
 
29964
30004
  render() {
29965
- return /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("div", {
30005
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(Heading, {
30006
+ title: "Common Graph Settings",
30007
+ isOpen: this.state.isExpanded,
30008
+ onToggle: () => this.setState({
30009
+ isExpanded: !this.state.isExpanded
30010
+ })
30011
+ }), this.state.isExpanded && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement("div", {
29966
30012
  className: "graph-settings"
29967
30013
  }, /*#__PURE__*/React__namespace.createElement("div", {
29968
30014
  className: "perseus-widget-row"
@@ -30053,10 +30099,10 @@ class InteractiveGraphSettings extends React__namespace.Component {
30053
30099
  onChange: this.change
30054
30100
  }))), /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30055
30101
  label: "Background image URL:",
30056
- style: styles$l.resetSpaceTop
30102
+ style: styles$o.resetSpaceTop
30057
30103
  }, /*#__PURE__*/React__namespace.createElement("input", {
30058
30104
  type: "text",
30059
- className: aphrodite.css(styles$l.backgroundUrlInput),
30105
+ className: aphrodite.css(styles$o.backgroundUrlInput),
30060
30106
  ref: this.bgUrlRef,
30061
30107
  value: this.state.backgroundImage.url || "",
30062
30108
  onChange: e => {
@@ -30072,28 +30118,28 @@ class InteractiveGraphSettings extends React__namespace.Component {
30072
30118
  onKeyPress: this.changeBackgroundUrl,
30073
30119
  onBlur: this.changeBackgroundUrl
30074
30120
  }), /*#__PURE__*/React__namespace.createElement(InfoTip$c, null, /*#__PURE__*/React__namespace.createElement("p", null, "Create an image in graphie, or use the \"Add image\" function to create a background."))), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30075
- style: styles$l.rulerSection
30121
+ style: styles$o.rulerSection
30076
30122
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30077
- style: styles$l.checkboxRow
30123
+ style: styles$o.checkboxRow
30078
30124
  }, /*#__PURE__*/React__namespace.createElement(PropCheckBox$7, {
30079
30125
  label: "Show ruler",
30080
30126
  showRuler: this.props.showRuler,
30081
30127
  onChange: this.change,
30082
- style: styles$l.resetSpaceTop
30128
+ style: styles$o.resetSpaceTop
30083
30129
  }), /*#__PURE__*/React__namespace.createElement(PropCheckBox$7, {
30084
30130
  label: "Show protractor",
30085
30131
  showProtractor: this.props.showProtractor,
30086
30132
  onChange: this.change,
30087
- style: styles$l.resetSpaceTop
30133
+ style: styles$o.resetSpaceTop
30088
30134
  })), (this.props.showRuler || this.props.showProtractor) && /*#__PURE__*/React__namespace.createElement(Banner__default["default"], {
30089
30135
  layout: "floating",
30090
30136
  text: "The ruler and protractor are not accessible. Please consider an alternate approach.",
30091
30137
  kind: "warning"
30092
30138
  }), this.props.showRuler && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30093
- style: styles$l.spaceTop
30139
+ style: styles$o.spaceTop
30094
30140
  }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30095
30141
  label: "Ruler label:",
30096
- style: styles$l.resetSpaceTop
30142
+ style: styles$o.resetSpaceTop
30097
30143
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30098
30144
  id: "ruler-label-select",
30099
30145
  selectedValue: this.props.rulerLabel,
@@ -30103,7 +30149,7 @@ class InteractiveGraphSettings extends React__namespace.Component {
30103
30149
  });
30104
30150
  },
30105
30151
  placeholder: "None",
30106
- style: styles$l.singleSelectShort
30152
+ style: styles$o.singleSelectShort
30107
30153
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30108
30154
  value: "",
30109
30155
  label: "None",
@@ -30144,15 +30190,15 @@ class InteractiveGraphSettings extends React__namespace.Component {
30144
30190
  });
30145
30191
  },
30146
30192
  placeholder: "10",
30147
- style: styles$l.singleSelectShort
30193
+ style: styles$o.singleSelectShort
30148
30194
  }, [1, 2, 4, 8, 10, 16].map(value => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30149
30195
  key: value,
30150
30196
  value: `${value}`,
30151
30197
  label: `${value}`
30152
- })))))));
30198
+ }))))))));
30153
30199
  }
30154
30200
  }
30155
- const styles$l = aphrodite.StyleSheet.create({
30201
+ const styles$o = aphrodite.StyleSheet.create({
30156
30202
  resetSpaceTop: {
30157
30203
  marginTop: 0
30158
30204
  },
@@ -30184,43 +30230,6 @@ const styles$l = aphrodite.StyleSheet.create({
30184
30230
  }
30185
30231
  });
30186
30232
 
30187
- /**
30188
- * Dropdown for selecting a locked figure to add to an interactive graph.
30189
- * Locked figures are elements (points, segmeents, etc.) that are not
30190
- * interactive, just present in the graph's background.
30191
- *
30192
- * Used in the interactive graph editor's locked figures section.
30193
- */
30194
- const LockedFigureSelect = props => {
30195
- const {
30196
- id,
30197
- onChange
30198
- } = props;
30199
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30200
- style: styles$k.container
30201
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionMenu, {
30202
- menuText: "Add locked figure",
30203
- style: styles$k.addElementSelect
30204
- }, [/*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionItem, {
30205
- key: `${id}-point`,
30206
- label: "Point",
30207
- onClick: () => onChange("point")
30208
- }, "Point"), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionItem, {
30209
- key: `${id}-line`,
30210
- label: "Line",
30211
- onClick: () => onChange("line")
30212
- }, "Line")]));
30213
- };
30214
- const styles$k = aphrodite.StyleSheet.create({
30215
- container: {
30216
- marginTop: wonderBlocksTokens.spacing.xSmall_8
30217
- },
30218
- addElementSelect: {
30219
- backgroundColor: wonderBlocksTokens.color.fadedBlue8,
30220
- borderRadius: wonderBlocksTokens.spacing.xxxSmall_4
30221
- }
30222
- });
30223
-
30224
30233
  const mediaDefaultSpecLargeMarginWidth = wonderBlocksTokens.spacing.large_24;
30225
30234
  const MEDIA_DEFAULT_SPEC = {
30226
30235
  small: {
@@ -30301,6 +30310,43 @@ const strutStyle = size => {
30301
30310
  };
30302
30311
  };
30303
30312
 
30313
+ /**
30314
+ * Dropdown for selecting a locked figure to add to an interactive graph.
30315
+ * Locked figures are elements (points, segmeents, etc.) that are not
30316
+ * interactive, just present in the graph's background.
30317
+ *
30318
+ * Used in the interactive graph editor's locked figures section.
30319
+ */
30320
+ const LockedFigureSelect = props => {
30321
+ const {
30322
+ id,
30323
+ onChange
30324
+ } = props;
30325
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30326
+ style: styles$n.container
30327
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionMenu, {
30328
+ menuText: "Add locked figure",
30329
+ style: styles$n.addElementSelect
30330
+ }, [/*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionItem, {
30331
+ key: `${id}-point`,
30332
+ label: "Point",
30333
+ onClick: () => onChange("point")
30334
+ }, "Point"), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionItem, {
30335
+ key: `${id}-line`,
30336
+ label: "Line",
30337
+ onClick: () => onChange("line")
30338
+ }, "Line")]));
30339
+ };
30340
+ const styles$n = aphrodite.StyleSheet.create({
30341
+ container: {
30342
+ marginTop: wonderBlocksTokens.spacing.xSmall_8
30343
+ },
30344
+ addElementSelect: {
30345
+ backgroundColor: wonderBlocksTokens.color.fadedBlue8,
30346
+ borderRadius: wonderBlocksTokens.spacing.xxxSmall_4
30347
+ }
30348
+ });
30349
+
30304
30350
  const ColorSwatch = props => {
30305
30351
  const {
30306
30352
  color,
@@ -30310,14 +30356,14 @@ const ColorSwatch = props => {
30310
30356
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30311
30357
  "aria-label":
30312
30358
  // Don't include an aria label for decorative circles.
30313
- !decorative ? `Color: ${color}, ${filled ? "filled" : "open"}` : undefined,
30314
- style: [styles$j.colorSwatch, {
30359
+ !decorative ? `${color}, ${filled ? "filled" : "open"}` : undefined,
30360
+ style: [styles$m.colorSwatch, {
30315
30361
  border: `4px solid ${perseus.lockedFigureColors[color]}`,
30316
30362
  backgroundColor: filled ? perseus.lockedFigureColors[color] : wonderBlocksTokens.color.white
30317
30363
  }]
30318
30364
  });
30319
30365
  };
30320
- const styles$j = aphrodite.StyleSheet.create({
30366
+ const styles$m = aphrodite.StyleSheet.create({
30321
30367
  colorSwatch: {
30322
30368
  // Add a white outline so that the color swatch is visible when
30323
30369
  // the dropdown option is highlighted with its blue background.
@@ -30331,16 +30377,17 @@ const styles$j = aphrodite.StyleSheet.create({
30331
30377
  const possibleColors = Object.keys(perseus.lockedFigureColors);
30332
30378
  const ColorSelect = props => {
30333
30379
  const {
30334
- id,
30335
30380
  selectedValue,
30336
30381
  style,
30337
30382
  onChange
30338
30383
  } = props;
30384
+ const ids = wonderBlocksCore.useUniqueIdWithMock();
30385
+ const id = ids.get("color-select");
30339
30386
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30340
- style: [styles$i.row, style]
30387
+ style: [styles$l.row, style]
30341
30388
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30342
30389
  htmlFor: id,
30343
- style: styles$i.label,
30390
+ style: styles$l.label,
30344
30391
  tag: "label"
30345
30392
  }, "color"), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30346
30393
  id: id,
@@ -30359,7 +30406,7 @@ const ColorSelect = props => {
30359
30406
  })
30360
30407
  }, colorName))));
30361
30408
  };
30362
- const styles$i = aphrodite.StyleSheet.create({
30409
+ const styles$l = aphrodite.StyleSheet.create({
30363
30410
  row: {
30364
30411
  flexDirection: "row",
30365
30412
  alignItems: "center"
@@ -30369,31 +30416,115 @@ const styles$i = aphrodite.StyleSheet.create({
30369
30416
  }
30370
30417
  });
30371
30418
 
30372
- const LineSwatch = props => {
30419
+ const CoordinatePairInput = props => {
30373
30420
  const {
30374
- color,
30375
- lineStyle
30421
+ coord,
30422
+ error,
30423
+ onChangeProps
30376
30424
  } = props;
30377
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30378
- style: styles$h.container
30379
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30380
- "aria-label": `${color}, ${lineStyle}`,
30381
- style: [styles$h.lineSwatch, {
30382
- border: `5px ${lineStyle} ${perseus.lockedFigureColors[color]}`
30383
- }]
30384
- }));
30425
+
30426
+ // Keep track of the coordinates via state as the user is editing them,
30427
+ // before they are updated in the props as a valid number.
30428
+ const [coordState, setCoordState] = React__namespace.useState([
30429
+ // Using strings to make it easier to work with the text fields.
30430
+ coord[0].toString(), coord[1].toString()]);
30431
+
30432
+ // Generate unique IDs so that the programmatic labels can be associated
30433
+ // with their respective text fields.
30434
+ const ids = wonderBlocksCore.useUniqueIdWithMock();
30435
+ const xCoordId = ids.get("x-coord");
30436
+ const yCoordId = ids.get("y-coord");
30437
+ function handleCoordChange(newValue, coordIndex) {
30438
+ // Update the local state (update the input field value).
30439
+ const newCoordState = [...coordState];
30440
+ newCoordState[coordIndex] = newValue;
30441
+ setCoordState(newCoordState);
30442
+
30443
+ // If the new value is not a number, don't update the props.
30444
+ // If it's empty, keep the props the same value instead of setting to 0.
30445
+ if (isNaN(+newValue) || newValue === "") {
30446
+ return;
30447
+ }
30448
+
30449
+ // Update the props (update the graph).
30450
+ const newCoords = [...coord];
30451
+ newCoords[coordIndex] = +newValue;
30452
+ onChangeProps({
30453
+ coord: newCoords
30454
+ });
30455
+ }
30456
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30457
+ style: [styles$k.row, styles$k.spaceUnder]
30458
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30459
+ htmlFor: xCoordId,
30460
+ style: styles$k.label,
30461
+ tag: "label"
30462
+ }, "x coord"), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
30463
+ id: xCoordId,
30464
+ type: "number",
30465
+ value: coordState[0],
30466
+ onChange: newValue => handleCoordChange(newValue, 0),
30467
+ style: [styles$k.textField, error ? styles$k.errorField : undefined]
30468
+ }), /*#__PURE__*/React__namespace.createElement(Strut, {
30469
+ size: wonderBlocksTokens.spacing.medium_16
30470
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30471
+ htmlFor: yCoordId,
30472
+ style: styles$k.label,
30473
+ tag: "label"
30474
+ }, "y coord"), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
30475
+ id: yCoordId,
30476
+ type: "number",
30477
+ value: coordState[1],
30478
+ onChange: newValue => handleCoordChange(newValue, 1),
30479
+ style: [styles$k.textField, error ? styles$k.errorField : undefined]
30480
+ })));
30385
30481
  };
30386
- const styles$h = aphrodite.StyleSheet.create({
30387
- container: {
30388
- backgroundColor: wonderBlocksTokens.color.white,
30389
- justifyContent: "center",
30390
- padding: wonderBlocksTokens.spacing.xSmall_8,
30391
- borderRadius: wonderBlocksTokens.spacing.xxxSmall_4
30482
+ const styles$k = aphrodite.StyleSheet.create({
30483
+ row: {
30484
+ flexDirection: "row",
30485
+ alignItems: "center"
30392
30486
  },
30393
- lineSwatch: {
30394
- // Add a white outline so that the color swatch is visible when
30395
- // the dropdown option is highlighted with its blue background.
30396
- width: 40
30487
+ spaceUnder: {
30488
+ marginBottom: wonderBlocksTokens.spacing.xSmall_8
30489
+ },
30490
+ label: {
30491
+ marginInlineEnd: wonderBlocksTokens.spacing.xxSmall_6
30492
+ },
30493
+ textField: {
30494
+ width: wonderBlocksTokens.spacing.xxxLarge_64
30495
+ },
30496
+ errorField: {
30497
+ borderColor: wonderBlocksTokens.color.red,
30498
+ backgroundColor: wonderBlocksTokens.color.fadedRed8
30499
+ }
30500
+ });
30501
+
30502
+ const LabeledSwitch = props => {
30503
+ const {
30504
+ checked,
30505
+ label,
30506
+ style,
30507
+ onChange
30508
+ } = props;
30509
+ const ids = wonderBlocksCore.useUniqueIdWithMock();
30510
+ const switchId = ids.get("switch");
30511
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30512
+ style: [styles$j.row, style]
30513
+ }, /*#__PURE__*/React__namespace.createElement(Switch__default["default"], {
30514
+ id: switchId,
30515
+ checked: checked,
30516
+ onChange: onChange
30517
+ }), /*#__PURE__*/React__namespace.createElement(Strut, {
30518
+ size: wonderBlocksTokens.spacing.xSmall_8
30519
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30520
+ tag: "label",
30521
+ htmlFor: switchId
30522
+ }, label));
30523
+ };
30524
+ const styles$j = aphrodite.StyleSheet.create({
30525
+ row: {
30526
+ flexDirection: "row",
30527
+ alignItems: "center"
30397
30528
  }
30398
30529
  });
30399
30530
 
@@ -30401,8 +30532,10 @@ const LockedFigureSettingsAccordion = props => {
30401
30532
  const {
30402
30533
  children,
30403
30534
  header,
30535
+ expanded,
30404
30536
  containerStyle,
30405
- panelStyle
30537
+ panelStyle,
30538
+ onToggle
30406
30539
  } = props;
30407
30540
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View
30408
30541
  // More specificity so that we can override the default
@@ -30411,14 +30544,16 @@ const LockedFigureSettingsAccordion = props => {
30411
30544
  , {
30412
30545
  className: "locked-figure-accordion"
30413
30546
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksAccordion.AccordionSection, {
30414
- style: [styles$g.container, containerStyle],
30415
- headerStyle: styles$g.accordionHeader,
30547
+ expanded: expanded,
30548
+ onToggle: onToggle,
30549
+ style: [styles$i.container, containerStyle],
30550
+ headerStyle: styles$i.accordionHeader,
30416
30551
  header: header
30417
30552
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30418
- style: [styles$g.accordionPanel, panelStyle]
30553
+ style: [styles$i.accordionPanel, panelStyle]
30419
30554
  }, children)));
30420
30555
  };
30421
- const styles$g = aphrodite.StyleSheet.create({
30556
+ const styles$i = aphrodite.StyleSheet.create({
30422
30557
  container: {
30423
30558
  backgroundColor: wonderBlocksTokens.color.fadedBlue8,
30424
30559
  marginTop: wonderBlocksTokens.spacing.xSmall_8
@@ -30440,170 +30575,56 @@ const styles$g = aphrodite.StyleSheet.create({
30440
30575
  });
30441
30576
 
30442
30577
  /**
30443
- * The part of a locked figure settings panel that contains actions
30444
- * for this panel and its associated locked figure, including
30445
- * the delete button.
30578
+ * DefiningPointSettings is a component that allows the user to edit the
30579
+ * settings of a point that defines a locked figure (ex. line) on the graph.
30580
+ *
30581
+ * Used in the interactive graph editor's locked figures section.
30446
30582
  */
30447
- const LockedFigureSettingsActions = props => {
30583
+ const DefiningPointSettings = props => {
30448
30584
  const {
30449
- onRemove,
30450
- figureAriaLabel
30585
+ coord,
30586
+ color: pointColor,
30587
+ filled = true,
30588
+ label,
30589
+ showPoint = "false",
30590
+ error,
30591
+ onChangeProps,
30592
+ onTogglePoint
30451
30593
  } = props;
30452
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30453
- style: styles$f.container
30454
- }, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
30455
- startIcon: trashIcon__default["default"],
30456
- "aria-label": `Delete ${figureAriaLabel}`,
30457
- onClick: onRemove,
30458
- kind: "tertiary",
30459
- style: styles$f.deleteButton
30460
- }, "Delete"));
30461
- };
30462
- const styles$f = aphrodite.StyleSheet.create({
30463
- container: {
30464
- width: "100%",
30465
- flexDirection: "row",
30466
- alignItems: "center",
30467
- marginTop: wonderBlocksTokens.spacing.xxxSmall_4
30468
- },
30469
- deleteButton: {
30470
- // Line up the delete icon with the rest of the content.
30471
- marginInlineStart: -wonderBlocksTokens.spacing.xxxSmall_4
30472
- }
30473
- });
30474
-
30475
- const LabeledSwitch = props => {
30476
- const {
30477
- checked,
30478
- label,
30479
- style,
30480
- onChange
30481
- } = props;
30482
- const ids = wonderBlocksCore.useUniqueIdWithMock();
30483
- const switchId = ids.get("switch");
30484
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30485
- style: [styles$e.row, style]
30486
- }, /*#__PURE__*/React__namespace.createElement(Switch__default["default"], {
30487
- id: switchId,
30488
- checked: checked,
30489
- onChange: onChange
30490
- }), /*#__PURE__*/React__namespace.createElement(Strut, {
30491
- size: wonderBlocksTokens.spacing.xSmall_8
30492
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30493
- tag: "label",
30494
- htmlFor: switchId
30495
- }, label));
30496
- };
30497
- const styles$e = aphrodite.StyleSheet.create({
30498
- row: {
30499
- flexDirection: "row",
30500
- alignItems: "center"
30501
- }
30502
- });
30503
-
30504
- /**
30505
- * LockedPointSettings is a component that allows the user to edit the
30506
- * settings of specifically a locked point on the graph.
30507
- *
30508
- * Used in the interactive graph editor's locked figures section.
30509
- */
30510
- const LockedPointSettings = props => {
30511
- const {
30512
- coord,
30513
- color: pointColor,
30514
- filled = true,
30515
- label,
30516
- toggled = "true",
30517
- style,
30518
- onChangeProps,
30519
- onToggle,
30520
- onRemove
30521
- } = props;
30522
-
30523
- // Keep track of the coordinates via state as the user is editing them,
30524
- // before they are updated in the props as a valid number.
30525
- const [coordState, setCoordState] = React__namespace.useState([
30526
- // Using strings to make it easier to work with the text fields.
30527
- coord[0].toString(), coord[1].toString()]);
30528
-
30529
- // Generate unique IDs so that the programmatic labels can be associated
30530
- // with their respective text fields.
30531
- const ids = wonderBlocksCore.useUniqueIdWithMock();
30532
- const xCoordId = ids.get("x-coord");
30533
- const yCoordId = ids.get("y-coord");
30534
- const colorSelectId = ids.get("point-color-select");
30535
- function handleCoordChange(newValue, coordIndex) {
30536
- // Update the local state (update the input field value).
30537
- const newCoordState = [...coordState];
30538
- newCoordState[coordIndex] = newValue;
30539
- setCoordState(newCoordState);
30540
-
30541
- // If the new value is not a number, don't update the props.
30542
- // If it's empty, keep the props the same value instead of setting to 0.
30543
- if (isNaN(+newValue) || newValue === "") {
30544
- return;
30545
- }
30546
-
30547
- // Update the props (update the graph).
30548
- const newCoords = [...coord];
30549
- newCoords[coordIndex] = +newValue;
30550
- onChangeProps({
30551
- coord: newCoords
30552
- });
30553
- }
30554
30594
  function handleColorChange(newValue) {
30555
30595
  onChangeProps({
30556
30596
  color: newValue
30557
30597
  });
30558
30598
  }
30559
30599
  return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30560
- containerStyle: style,
30561
- panelStyle: !onRemove && styles$d.accordionPanelWithoutActions,
30600
+ expanded: props.expanded,
30601
+ onToggle: props.onToggle,
30602
+ containerStyle: styles$h.container,
30603
+ panelStyle: styles$h.accordionPanel,
30562
30604
  header:
30563
30605
  /*#__PURE__*/
30564
30606
  // Summary: Point, coords, color (filled/open)
30565
30607
  React__namespace.createElement(wonderBlocksCore.View, {
30566
- style: styles$d.row
30608
+ style: styles$h.row
30567
30609
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `${label || "Point"} (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React__namespace.createElement(Strut, {
30568
30610
  size: wonderBlocksTokens.spacing.xSmall_8
30569
- }), toggled && /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
30611
+ }), showPoint && /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
30570
30612
  color: pointColor,
30571
30613
  filled: filled
30572
30614
  }))
30573
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30574
- style: [styles$d.row, styles$d.spaceUnder]
30575
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30576
- htmlFor: xCoordId,
30577
- style: styles$d.label,
30578
- tag: "label"
30579
- }, "x coord"), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
30580
- id: xCoordId,
30581
- type: "number",
30582
- value: coordState[0],
30583
- onChange: newValue => handleCoordChange(newValue, 0),
30584
- style: styles$d.textField
30585
- }), /*#__PURE__*/React__namespace.createElement(Strut, {
30586
- size: wonderBlocksTokens.spacing.medium_16
30587
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30588
- htmlFor: yCoordId,
30589
- style: styles$d.label,
30590
- tag: "label"
30591
- }, "y coord"), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
30592
- id: yCoordId,
30593
- type: "number",
30594
- value: coordState[1],
30595
- onChange: newValue => handleCoordChange(newValue, 1),
30596
- style: styles$d.textField
30597
- })), onToggle && /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30615
+ }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30616
+ coord: coord,
30617
+ error: !!error,
30618
+ onChangeProps: onChangeProps
30619
+ }), onTogglePoint && /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30598
30620
  label: "show point on graph",
30599
- checked: !!toggled,
30600
- style: toggled && styles$d.spaceUnder,
30601
- onChange: onToggle
30602
- }), toggled && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30603
- id: colorSelectId,
30621
+ checked: !!showPoint,
30622
+ style: showPoint && styles$h.spaceUnder,
30623
+ onChange: onTogglePoint
30624
+ }), showPoint && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30604
30625
  selectedValue: pointColor,
30605
30626
  onChange: handleColorChange,
30606
- style: styles$d.spaceUnder
30627
+ style: styles$h.spaceUnder
30607
30628
  }), /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30608
30629
  label: "open point",
30609
30630
  checked: !filled,
@@ -30612,13 +30633,17 @@ const LockedPointSettings = props => {
30612
30633
  filled: !newValue
30613
30634
  });
30614
30635
  }
30615
- })), onRemove && /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30616
- onRemove: onRemove,
30617
- figureAriaLabel: `locked point at ${coord[0]}, ${coord[1]}`
30618
- }));
30636
+ })));
30619
30637
  };
30620
- const styles$d = aphrodite.StyleSheet.create({
30621
- accordionPanelWithoutActions: {
30638
+ const styles$h = aphrodite.StyleSheet.create({
30639
+ container: {
30640
+ marginTop: wonderBlocksTokens.spacing.xSmall_8,
30641
+ marginBottom: 0,
30642
+ marginLeft: -wonderBlocksTokens.spacing.xxxSmall_4,
30643
+ marginRight: -wonderBlocksTokens.spacing.xxxSmall_4,
30644
+ backgroundColor: wonderBlocksTokens.color.white
30645
+ },
30646
+ accordionPanel: {
30622
30647
  // Need more space since we don't have the actions' margins.
30623
30648
  paddingBottom: wonderBlocksTokens.spacing.medium_16
30624
30649
  },
@@ -30628,15 +30653,69 @@ const styles$d = aphrodite.StyleSheet.create({
30628
30653
  },
30629
30654
  spaceUnder: {
30630
30655
  marginBottom: wonderBlocksTokens.spacing.xSmall_8
30656
+ }
30657
+ });
30658
+
30659
+ const LineSwatch = props => {
30660
+ const {
30661
+ color,
30662
+ lineStyle
30663
+ } = props;
30664
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30665
+ style: styles$g.container
30666
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30667
+ "aria-label": `${color}, ${lineStyle}`,
30668
+ style: [styles$g.lineSwatch, {
30669
+ border: `5px ${lineStyle} ${perseus.lockedFigureColors[color]}`
30670
+ }]
30671
+ }));
30672
+ };
30673
+ const styles$g = aphrodite.StyleSheet.create({
30674
+ container: {
30675
+ backgroundColor: wonderBlocksTokens.color.white,
30676
+ justifyContent: "center",
30677
+ padding: wonderBlocksTokens.spacing.xSmall_8,
30678
+ borderRadius: wonderBlocksTokens.spacing.xxxSmall_4
30631
30679
  },
30632
- label: {
30633
- marginInlineEnd: wonderBlocksTokens.spacing.xxSmall_6
30680
+ lineSwatch: {
30681
+ width: 40
30682
+ }
30683
+ });
30684
+
30685
+ /**
30686
+ * The part of a locked figure settings panel that contains actions
30687
+ * for this panel and its associated locked figure, including
30688
+ * the delete button.
30689
+ */
30690
+ const LockedFigureSettingsActions = props => {
30691
+ const {
30692
+ onRemove,
30693
+ figureAriaLabel
30694
+ } = props;
30695
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30696
+ style: styles$f.container
30697
+ }, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
30698
+ startIcon: trashIcon__default["default"],
30699
+ "aria-label": `Delete ${figureAriaLabel}`,
30700
+ onClick: onRemove,
30701
+ kind: "tertiary",
30702
+ style: styles$f.deleteButton
30703
+ }, "Delete"));
30704
+ };
30705
+ const styles$f = aphrodite.StyleSheet.create({
30706
+ container: {
30707
+ width: "100%",
30708
+ flexDirection: "row",
30709
+ alignItems: "center",
30710
+ marginTop: wonderBlocksTokens.spacing.xxxSmall_4
30634
30711
  },
30635
- textField: {
30636
- width: wonderBlocksTokens.spacing.xxxLarge_64
30712
+ deleteButton: {
30713
+ // Line up the delete icon with the rest of the content.
30714
+ marginInlineStart: -wonderBlocksTokens.spacing.xxxSmall_4
30637
30715
  }
30638
30716
  });
30639
30717
 
30718
+ const lengthZeroStr = "The line cannot have length 0.";
30640
30719
  const LockedLineSettings = props => {
30641
30720
  const {
30642
30721
  kind,
@@ -30654,11 +30733,11 @@ const LockedLineSettings = props => {
30654
30733
  // with their respective text fields.
30655
30734
  const ids = wonderBlocksCore.useUniqueIdWithMock();
30656
30735
  const kindSelectId = ids.get("line-kind-select");
30657
- const colorSelectId = ids.get("line-color-select");
30658
30736
  const styleSelectId = ids.get("line-style-select");
30659
30737
  const capitalizeKind = kind.charAt(0).toUpperCase() + kind.slice(1);
30660
30738
  const lineLabel = `${capitalizeKind} (${point1.coord[0]},
30661
30739
  ${point1.coord[1]}), (${point2.coord[0]}, ${point2.coord[1]})`;
30740
+ const isInvalid = point1.coord[0] === point2.coord[0] && point1.coord[1] === point2.coord[1];
30662
30741
  function handleChangePoint(newPointProps, index) {
30663
30742
  const newPoints = [...points];
30664
30743
  newPoints[index] = {
@@ -30683,8 +30762,10 @@ const LockedLineSettings = props => {
30683
30762
  });
30684
30763
  }
30685
30764
  return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30765
+ expanded: props.expanded,
30766
+ onToggle: props.onToggle,
30686
30767
  header: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30687
- style: styles$c.row
30768
+ style: styles$e.row
30688
30769
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, lineLabel), /*#__PURE__*/React__namespace.createElement(Strut, {
30689
30770
  size: wonderBlocksTokens.spacing.xSmall_8
30690
30771
  }), /*#__PURE__*/React__namespace.createElement(LineSwatch, {
@@ -30692,10 +30773,10 @@ const LockedLineSettings = props => {
30692
30773
  lineStyle: lineStyle
30693
30774
  }))
30694
30775
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30695
- style: [styles$c.row, styles$c.spaceUnder]
30776
+ style: [styles$e.row, styles$e.spaceUnder]
30696
30777
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30697
30778
  htmlFor: kindSelectId,
30698
- style: styles$c.label,
30779
+ style: styles$e.label,
30699
30780
  tag: "label"
30700
30781
  }, "kind"), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30701
30782
  id: kindSelectId,
@@ -30709,25 +30790,24 @@ const LockedLineSettings = props => {
30709
30790
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30710
30791
  value: "line",
30711
30792
  label: "line"
30712
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30713
- value: "segment",
30714
- label: "segment"
30715
30793
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30716
30794
  value: "ray",
30717
30795
  label: "ray"
30796
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30797
+ value: "segment",
30798
+ label: "segment"
30718
30799
  }))), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30719
- style: [styles$c.row, styles$c.spaceUnder]
30800
+ style: [styles$e.row, styles$e.spaceUnder]
30720
30801
  }, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30721
- id: colorSelectId,
30722
30802
  selectedValue: lineColor,
30723
30803
  onChange: handleColorChange
30724
30804
  }), /*#__PURE__*/React__namespace.createElement(Strut, {
30725
30805
  size: wonderBlocksTokens.spacing.small_12
30726
30806
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30727
- style: styles$c.row
30807
+ style: styles$e.row
30728
30808
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30729
30809
  htmlFor: styleSelectId,
30730
- style: styles$c.label,
30810
+ style: styles$e.label,
30731
30811
  tag: "label"
30732
30812
  }, "style"), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30733
30813
  id: styleSelectId,
@@ -30738,31 +30818,33 @@ const LockedLineSettings = props => {
30738
30818
  // Placeholder is required, but never gets used.
30739
30819
  ,
30740
30820
  placeholder: "",
30741
- style: styles$c.selectMarginOffset
30821
+ style: styles$e.selectMarginOffset
30742
30822
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30743
30823
  value: "solid",
30744
30824
  label: "solid"
30745
30825
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30746
30826
  value: "dashed",
30747
30827
  label: "dashed"
30748
- })))), /*#__PURE__*/React__namespace.createElement(LockedPointSettings, _extends({
30828
+ })))), isInvalid && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30829
+ style: styles$e.errorText
30830
+ }, lengthZeroStr), /*#__PURE__*/React__namespace.createElement(DefiningPointSettings, _extends({
30749
30831
  label: "Point 1",
30750
- toggled: showPoint1
30832
+ showPoint: showPoint1,
30833
+ error: isInvalid ? lengthZeroStr : null
30751
30834
  }, point1, {
30752
- onToggle: newValue => onChangeProps({
30835
+ onTogglePoint: newValue => onChangeProps({
30753
30836
  showPoint1: newValue
30754
30837
  }),
30755
- onChangeProps: newProps => handleChangePoint(newProps, 0),
30756
- style: styles$c.lockedPointSettingsContainer
30757
- })), /*#__PURE__*/React__namespace.createElement(LockedPointSettings, _extends({
30838
+ onChangeProps: newProps => handleChangePoint(newProps, 0)
30839
+ })), /*#__PURE__*/React__namespace.createElement(DefiningPointSettings, _extends({
30758
30840
  label: "Point 2",
30759
- toggled: showPoint2
30841
+ showPoint: showPoint2,
30842
+ error: isInvalid ? lengthZeroStr : null
30760
30843
  }, point2, {
30761
- onToggle: newValue => onChangeProps({
30844
+ onTogglePoint: newValue => onChangeProps({
30762
30845
  showPoint2: newValue
30763
30846
  }),
30764
- onChangeProps: newProps => handleChangePoint(newProps, 1),
30765
- style: styles$c.lockedPointSettingsContainer
30847
+ onChangeProps: newProps => handleChangePoint(newProps, 1)
30766
30848
  })), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30767
30849
  onRemove: onRemove,
30768
30850
  figureAriaLabel: `locked line defined by
@@ -30770,14 +30852,7 @@ const LockedLineSettings = props => {
30770
30852
  ${point2.coord[0]}, ${point2.coord[1]}.`
30771
30853
  }));
30772
30854
  };
30773
- const styles$c = aphrodite.StyleSheet.create({
30774
- lockedPointSettingsContainer: {
30775
- marginTop: wonderBlocksTokens.spacing.xSmall_8,
30776
- marginBottom: 0,
30777
- marginLeft: -wonderBlocksTokens.spacing.xxxSmall_4,
30778
- marginRight: -wonderBlocksTokens.spacing.xxxSmall_4,
30779
- backgroundColor: wonderBlocksTokens.color.white
30780
- },
30855
+ const styles$e = aphrodite.StyleSheet.create({
30781
30856
  row: {
30782
30857
  flexDirection: "row",
30783
30858
  alignItems: "center"
@@ -30791,6 +30866,72 @@ const styles$c = aphrodite.StyleSheet.create({
30791
30866
  selectMarginOffset: {
30792
30867
  // Align with the point settings accordions.
30793
30868
  marginInlineEnd: -wonderBlocksTokens.spacing.xxxSmall_4
30869
+ },
30870
+ errorText: {
30871
+ color: wonderBlocksTokens.color.red
30872
+ }
30873
+ });
30874
+
30875
+ /**
30876
+ * LockedPointSettings is a component that allows the user to edit the
30877
+ * settings of specifically a locked point on the graph.
30878
+ *
30879
+ * Used in the interactive graph editor's locked figures section.
30880
+ */
30881
+ const LockedPointSettings = props => {
30882
+ const {
30883
+ coord,
30884
+ color: pointColor,
30885
+ filled = true,
30886
+ onChangeProps,
30887
+ onRemove
30888
+ } = props;
30889
+ function handleColorChange(newValue) {
30890
+ onChangeProps({
30891
+ color: newValue
30892
+ });
30893
+ }
30894
+ return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30895
+ expanded: props.expanded,
30896
+ onToggle: props.onToggle,
30897
+ header:
30898
+ /*#__PURE__*/
30899
+ // Summary: Point, coords, color (filled/open)
30900
+ React__namespace.createElement(wonderBlocksCore.View, {
30901
+ style: styles$d.row
30902
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Point (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React__namespace.createElement(Strut, {
30903
+ size: wonderBlocksTokens.spacing.xSmall_8
30904
+ }), /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
30905
+ color: pointColor,
30906
+ filled: filled
30907
+ }))
30908
+ }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30909
+ coord: coord,
30910
+ onChangeProps: onChangeProps
30911
+ }), /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30912
+ selectedValue: pointColor,
30913
+ onChange: handleColorChange,
30914
+ style: styles$d.spaceUnder
30915
+ }), /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30916
+ label: "open point",
30917
+ checked: !filled,
30918
+ onChange: newValue => {
30919
+ onChangeProps({
30920
+ filled: !newValue
30921
+ });
30922
+ }
30923
+ }), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30924
+ onRemove: onRemove,
30925
+ figureAriaLabel: `locked point at ${coord[0]}, ${coord[1]}`
30926
+ }));
30927
+ };
30928
+ const styles$d = aphrodite.StyleSheet.create({
30929
+ row: {
30930
+ flexDirection: "row",
30931
+ alignItems: "center"
30932
+ },
30933
+ spaceUnder: {
30934
+ marginBottom: wonderBlocksTokens.spacing.xSmall_8
30794
30935
  }
30795
30936
  });
30796
30937
 
@@ -30882,6 +31023,10 @@ function getDefaultFigureForType(type) {
30882
31023
  }
30883
31024
 
30884
31025
  const LockedFiguresSection = props => {
31026
+ // Keep track of all figures' accordions' expanded states for the
31027
+ // expand/collapse all button. Set the whole array to false initially.
31028
+ const collapsedStateArray = Array((props.figures ?? []).length).fill(false);
31029
+ const [expandedStates, setExpandedStates] = React__namespace.useState(collapsedStateArray);
30885
31030
  const uniqueId = wonderBlocksCore.useUniqueIdWithMock().get("locked-figures-section");
30886
31031
  const {
30887
31032
  figures,
@@ -30893,6 +31038,7 @@ const LockedFiguresSection = props => {
30893
31038
  lockedFigures: [...lockedFigures, getDefaultFigureForType(newFigure)]
30894
31039
  };
30895
31040
  onChange(newProps);
31041
+ setExpandedStates([...expandedStates, true]);
30896
31042
  }
30897
31043
  function removeLockedFigure(index) {
30898
31044
  // eslint-disable-next-line no-alert
@@ -30901,6 +31047,11 @@ const LockedFiguresSection = props => {
30901
31047
  onChange({
30902
31048
  lockedFigures: [...lockedFigures.slice(0, index), ...lockedFigures.slice(index + 1)]
30903
31049
  });
31050
+
31051
+ // Update expanded states
31052
+ const newExpandedStates = [...expandedStates];
31053
+ newExpandedStates.splice(index, 1);
31054
+ setExpandedStates(newExpandedStates);
30904
31055
  }
30905
31056
  }
30906
31057
  function changeProps(index,
@@ -30916,16 +31067,46 @@ const LockedFiguresSection = props => {
30916
31067
  };
30917
31068
  onChange(newFigures);
30918
31069
  }
31070
+ function toggleExpanded(newValue) {
31071
+ setExpandedStates(Array(figures?.length).fill(newValue));
31072
+ }
31073
+ const allCollapsed = expandedStates.every(value => !value);
31074
+ const buttonLabel = allCollapsed ? "Expand all" : "Collapse all";
31075
+ const showExpandButton = !!figures?.length;
30919
31076
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, figures?.map((figure, index) => /*#__PURE__*/React__namespace.createElement(LockedFigureSettings, _extends({
31077
+ expanded: expandedStates[index],
31078
+ onToggle: newValue => {
31079
+ const newExpanded = [...expandedStates];
31080
+ newExpanded[index] = newValue;
31081
+ setExpandedStates(newExpanded);
31082
+ },
30920
31083
  key: `${uniqueId}-locked-${figure}-${index}`
30921
31084
  }, figure, {
30922
31085
  onChangeProps: newProps => changeProps(index, newProps),
30923
31086
  onRemove: () => removeLockedFigure(index)
30924
- }))), /*#__PURE__*/React__namespace.createElement(LockedFigureSelect, {
31087
+ }))), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31088
+ style: styles$c.buttonContainer
31089
+ }, /*#__PURE__*/React__namespace.createElement(LockedFigureSelect, {
30925
31090
  id: `${uniqueId}-select`,
30926
31091
  onChange: addLockedFigure
30927
- }));
31092
+ }), /*#__PURE__*/React__namespace.createElement(Strut, {
31093
+ size: wonderBlocksTokens.spacing.small_12
31094
+ }), showExpandButton && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
31095
+ kind: "secondary",
31096
+ onClick: () => toggleExpanded(allCollapsed),
31097
+ style: styles$c.button
31098
+ }, buttonLabel)));
30928
31099
  };
31100
+ const styles$c = aphrodite.StyleSheet.create({
31101
+ buttonContainer: {
31102
+ flexDirection: "row",
31103
+ alignItems: "center"
31104
+ },
31105
+ button: {
31106
+ marginTop: wonderBlocksTokens.spacing.xSmall_8,
31107
+ flexGrow: 1
31108
+ }
31109
+ });
30929
31110
 
30930
31111
  const SegmentCountSelector = _ref => {
30931
31112
  let {