@khanacademy/perseus-editor 6.10.0 → 6.12.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.
Files changed (27) hide show
  1. package/dist/components/{coordinate-pair-input.d.ts → graph-locked-figures/coordinate-pair-input.d.ts} +2 -0
  2. package/dist/components/{defining-point-settings.d.ts → graph-locked-figures/defining-point-settings.d.ts} +9 -2
  3. package/dist/components/{ellipse-swatch.d.ts → graph-locked-figures/ellipse-swatch.d.ts} +2 -2
  4. package/dist/components/{locked-ellipse-settings.d.ts → graph-locked-figures/locked-ellipse-settings.d.ts} +2 -6
  5. package/dist/components/{locked-figure-select.d.ts → graph-locked-figures/locked-figure-select.d.ts} +1 -0
  6. package/dist/components/graph-locked-figures/locked-figure-settings-actions.d.ts +11 -0
  7. package/dist/components/{locked-figure-settings.d.ts → graph-locked-figures/locked-figure-settings.d.ts} +13 -2
  8. package/dist/components/{locked-figures-section.d.ts → graph-locked-figures/locked-figures-section.d.ts} +2 -1
  9. package/dist/components/{locked-line-settings.d.ts → graph-locked-figures/locked-line-settings.d.ts} +2 -6
  10. package/dist/components/{locked-point-settings.d.ts → graph-locked-figures/locked-point-settings.d.ts} +2 -6
  11. package/dist/components/graph-locked-figures/locked-polygon-settings.d.ts +11 -0
  12. package/dist/components/{locked-vector-settings.d.ts → graph-locked-figures/locked-vector-settings.d.ts} +2 -6
  13. package/dist/components/graph-locked-figures/polygon-swatch.d.ts +9 -0
  14. package/dist/components/util.d.ts +5 -2
  15. package/dist/es/index.js +2000 -1701
  16. package/dist/es/index.js.map +1 -1
  17. package/dist/index.js +2051 -1746
  18. package/dist/index.js.map +1 -1
  19. package/dist/widgets/interactive-graph-editor.d.ts +402 -233
  20. package/package.json +2 -2
  21. package/dist/components/locked-figure-settings-actions.d.ts +0 -7
  22. /package/dist/components/{color-select.d.ts → graph-locked-figures/color-select.d.ts} +0 -0
  23. /package/dist/components/{color-swatch.d.ts → graph-locked-figures/color-swatch.d.ts} +0 -0
  24. /package/dist/components/{labeled-row.d.ts → graph-locked-figures/labeled-row.d.ts} +0 -0
  25. /package/dist/components/{labeled-switch.d.ts → graph-locked-figures/labeled-switch.d.ts} +0 -0
  26. /package/dist/components/{line-swatch.d.ts → graph-locked-figures/line-swatch.d.ts} +0 -0
  27. /package/dist/components/{locked-figure-settings-accordion.d.ts → graph-locked-figures/locked-figure-settings-accordion.d.ts} +0 -0
package/dist/index.js CHANGED
@@ -24,11 +24,18 @@ var mathInput = require('@khanacademy/math-input');
24
24
  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
- var Banner = require('@khanacademy/wonder-blocks-banner');
28
27
  var Button = require('@khanacademy/wonder-blocks-button');
29
- var Switch = require('@khanacademy/wonder-blocks-switch');
30
28
  var wonderBlocksAccordion = require('@khanacademy/wonder-blocks-accordion');
29
+ var IconButton = require('@khanacademy/wonder-blocks-icon-button');
30
+ var caretDoubleDownIcon = require('@phosphor-icons/core/bold/caret-double-down-bold.svg');
31
+ var caretDoubleUpIcon = require('@phosphor-icons/core/bold/caret-double-up-bold.svg');
32
+ var caretDownIcon = require('@phosphor-icons/core/bold/caret-down-bold.svg');
33
+ var caretUpIcon = require('@phosphor-icons/core/bold/caret-up-bold.svg');
31
34
  var trashIcon = require('@phosphor-icons/core/bold/trash-bold.svg');
35
+ var Switch = require('@khanacademy/wonder-blocks-switch');
36
+ var minusCircle = require('@phosphor-icons/core/regular/minus-circle.svg');
37
+ var plusCircle = require('@phosphor-icons/core/regular/plus-circle.svg');
38
+ var Banner = require('@khanacademy/wonder-blocks-banner');
32
39
 
33
40
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
34
41
 
@@ -60,14 +67,21 @@ var caretRight__default = /*#__PURE__*/_interopDefaultLegacy(caretRight);
60
67
  var classNames__default = /*#__PURE__*/_interopDefaultLegacy(classNames);
61
68
  var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
62
69
  var KAS__namespace = /*#__PURE__*/_interopNamespace(KAS);
63
- var Banner__default = /*#__PURE__*/_interopDefaultLegacy(Banner);
64
70
  var Button__default = /*#__PURE__*/_interopDefaultLegacy(Button);
65
- var Switch__default = /*#__PURE__*/_interopDefaultLegacy(Switch);
71
+ var IconButton__default = /*#__PURE__*/_interopDefaultLegacy(IconButton);
72
+ var caretDoubleDownIcon__default = /*#__PURE__*/_interopDefaultLegacy(caretDoubleDownIcon);
73
+ var caretDoubleUpIcon__default = /*#__PURE__*/_interopDefaultLegacy(caretDoubleUpIcon);
74
+ var caretDownIcon__default = /*#__PURE__*/_interopDefaultLegacy(caretDownIcon);
75
+ var caretUpIcon__default = /*#__PURE__*/_interopDefaultLegacy(caretUpIcon);
66
76
  var trashIcon__default = /*#__PURE__*/_interopDefaultLegacy(trashIcon);
77
+ var Switch__default = /*#__PURE__*/_interopDefaultLegacy(Switch);
78
+ var minusCircle__default = /*#__PURE__*/_interopDefaultLegacy(minusCircle);
79
+ var plusCircle__default = /*#__PURE__*/_interopDefaultLegacy(plusCircle);
80
+ var Banner__default = /*#__PURE__*/_interopDefaultLegacy(Banner);
67
81
 
68
82
  // This file is processed by a Rollup plugin (replace) to inject the production
69
83
  const libName = "@khanacademy/perseus-editor";
70
- const libVersion = "6.10.0";
84
+ const libVersion = "6.12.0";
71
85
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
72
86
 
73
87
  function _extends() {
@@ -262,13 +276,13 @@ class SectionControlButton extends React__namespace.Component {
262
276
  role: "button",
263
277
  hideDefaultFocusRing: true,
264
278
  "aria-label": title,
265
- style: styles$D.button
279
+ style: styles$G.button
266
280
  }, _ref => {
267
281
  return /*#__PURE__*/React__namespace.createElement(InlineIcon$b, icon);
268
282
  });
269
283
  }
270
284
  }
271
- const styles$D = aphrodite.StyleSheet.create({
285
+ const styles$G = aphrodite.StyleSheet.create({
272
286
  button: {
273
287
  backgroundColor: wonderBlocksTokens.color.gold,
274
288
  margin: 2,
@@ -20773,13 +20787,13 @@ class WidgetSelect extends React__namespace.Component {
20773
20787
  * that collapse a region.
20774
20788
  */
20775
20789
  function ToggleableCaret(props) {
20776
- const iconStyle = props.isExpanded ? styles$C.expanded : styles$C.collapsed;
20790
+ const iconStyle = props.isExpanded ? styles$F.expanded : styles$F.collapsed;
20777
20791
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksIcon.PhosphorIcon, {
20778
20792
  icon: caretRight__default["default"],
20779
20793
  style: iconStyle
20780
20794
  });
20781
20795
  }
20782
- const styles$C = aphrodite.StyleSheet.create({
20796
+ const styles$F = aphrodite.StyleSheet.create({
20783
20797
  collapsed: {
20784
20798
  transition: ".15s"
20785
20799
  },
@@ -20809,16 +20823,16 @@ class TexErrorView extends React__namespace.Component {
20809
20823
  showErrors
20810
20824
  } = this.state;
20811
20825
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
20812
- style: styles$B.errorContainer
20826
+ style: styles$E.errorContainer
20813
20827
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
20814
- style: styles$B.title,
20828
+ style: styles$E.title,
20815
20829
  onClick: this.handleToggleTexErrors
20816
20830
  }, /*#__PURE__*/React__namespace.createElement(ToggleableCaret, {
20817
20831
  isExpanded: showErrors
20818
20832
  }), "\xA0 TeX Errors (", errorList.length, ")"), showErrors && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
20819
- style: styles$B.errorExplanation
20833
+ style: styles$E.errorExplanation
20820
20834
  }, "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, {
20821
- style: styles$B.error,
20835
+ style: styles$E.error,
20822
20836
  key: index
20823
20837
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
20824
20838
  style: {
@@ -20827,7 +20841,7 @@ class TexErrorView extends React__namespace.Component {
20827
20841
  }, e.math), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, e.message))));
20828
20842
  }
20829
20843
  }
20830
- const styles$B = aphrodite.StyleSheet.create({
20844
+ const styles$E = aphrodite.StyleSheet.create({
20831
20845
  title: {
20832
20846
  backgroundColor: "#eee",
20833
20847
  fontSize: "1.25em",
@@ -23464,7 +23478,7 @@ class StructuredItemDiff extends React__namespace.Component {
23464
23478
 
23465
23479
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
23466
23480
  const {
23467
- InfoTip: InfoTip$n,
23481
+ InfoTip: InfoTip$m,
23468
23482
  InlineIcon: InlineIcon$7
23469
23483
  } = perseus.components;
23470
23484
  const {
@@ -23538,7 +23552,7 @@ class HintEditor extends React__namespace.Component {
23538
23552
  type: "button",
23539
23553
  className: this.props.isFirst ? "hidden" : "",
23540
23554
  onClick: ___default["default"].partial(this.props.onMove, -1)
23541
- }, /*#__PURE__*/React__namespace.createElement(InlineIcon$7, iconCircleArrowUp)), " ", this.props.isLast && /*#__PURE__*/React__namespace.createElement(InfoTip$n, null, /*#__PURE__*/React__namespace.createElement("p", null, "The last hint is automatically bolded."))), /*#__PURE__*/React__namespace.createElement("input", {
23555
+ }, /*#__PURE__*/React__namespace.createElement(InlineIcon$7, iconCircleArrowUp)), " ", this.props.isLast && /*#__PURE__*/React__namespace.createElement(InfoTip$m, null, /*#__PURE__*/React__namespace.createElement("p", null, "The last hint is automatically bolded."))), /*#__PURE__*/React__namespace.createElement("input", {
23542
23556
  type: "checkbox"
23543
23557
  // @ts-expect-error - TS2322 - Type 'boolean | null | undefined' is not assignable to type 'boolean | undefined'.
23544
23558
  ,
@@ -23773,7 +23787,7 @@ class CombinedHintsEditor extends React__namespace.Component {
23773
23787
  }
23774
23788
 
23775
23789
  const {
23776
- InfoTip: InfoTip$m
23790
+ InfoTip: InfoTip$l
23777
23791
  } = perseus.components;
23778
23792
  class ItemExtrasEditor extends React__namespace.Component {
23779
23793
  static defaultProps = {
@@ -23918,7 +23932,7 @@ const ItemExtraCheckbox = props => /*#__PURE__*/React__namespace.createElement("
23918
23932
  type: "checkbox",
23919
23933
  checked: props.checked,
23920
23934
  onChange: props.onChange
23921
- })), /*#__PURE__*/React__namespace.createElement(InfoTip$m, null, props.infoTip));
23935
+ })), /*#__PURE__*/React__namespace.createElement(InfoTip$l, null, props.infoTip));
23922
23936
 
23923
23937
  const ITEM_DATA_VERSION = perseus.itemDataVersion;
23924
23938
  class ItemEditor extends React__namespace.Component {
@@ -24577,12 +24591,12 @@ class SimpleButton extends React__namespace.Component {
24577
24591
  ...otherProps
24578
24592
  } = this.props;
24579
24593
  return /*#__PURE__*/React__namespace.createElement("div", _extends({
24580
- className: aphrodite.css(styles$A.baseButton, color === "green" && styles$A.green, color === "orange" && styles$A.orange),
24594
+ className: aphrodite.css(styles$D.baseButton, color === "green" && styles$D.green, color === "orange" && styles$D.orange),
24581
24595
  onClick: onClick
24582
24596
  }, otherProps), children);
24583
24597
  }
24584
24598
  }
24585
- const styles$A = aphrodite.StyleSheet.create({
24599
+ const styles$D = aphrodite.StyleSheet.create({
24586
24600
  baseButton: {
24587
24601
  top: "0",
24588
24602
  fontSize: "11px",
@@ -24795,23 +24809,23 @@ const LeafContainer = _ref2 => {
24795
24809
  } = _ref2;
24796
24810
  const hasPreviewHeading = shape.type === "content" || shape.type === "hint";
24797
24811
  const previewHeading = hasPreviewHeading && /*#__PURE__*/React__namespace.createElement("div", {
24798
- className: aphrodite.css(styles$z.containerHeader)
24812
+ className: aphrodite.css(styles$C.containerHeader)
24799
24813
  }, /*#__PURE__*/React__namespace.createElement(Header, {
24800
24814
  depth: path.length,
24801
- className: aphrodite.css(styles$z.containerTitle)
24815
+ className: aphrodite.css(styles$C.containerTitle)
24802
24816
  }, capitalize(name)));
24803
24817
  return /*#__PURE__*/React__namespace.createElement("div", {
24804
- className: aphrodite.css(styles$z.container)
24818
+ className: aphrodite.css(styles$C.container)
24805
24819
  }, /*#__PURE__*/React__namespace.createElement("span", {
24806
- className: aphrodite.css(styles$z.row, styles$z.rowHeading)
24820
+ className: aphrodite.css(styles$C.row, styles$C.rowHeading)
24807
24821
  }, /*#__PURE__*/React__namespace.createElement("div", {
24808
- className: aphrodite.css(styles$z.columnLeft)
24822
+ className: aphrodite.css(styles$C.columnLeft)
24809
24823
  }, /*#__PURE__*/React__namespace.createElement("div", {
24810
- className: "pod-title " + aphrodite.css(styles$z.containerHeader)
24824
+ className: "pod-title " + aphrodite.css(styles$C.containerHeader)
24811
24825
  }, /*#__PURE__*/React__namespace.createElement("div", {
24812
- className: aphrodite.css(styles$z.containerTitle)
24826
+ className: aphrodite.css(styles$C.containerTitle)
24813
24827
  }, capitalize(name)), controls)), /*#__PURE__*/React__namespace.createElement("div", {
24814
- className: aphrodite.css(styles$z.columnRight)
24828
+ className: aphrodite.css(styles$C.columnRight)
24815
24829
  }, previewHeading)), children);
24816
24830
  };
24817
24831
  const ArrayContainer = props => {
@@ -24824,11 +24838,11 @@ const ArrayContainer = props => {
24824
24838
  actions
24825
24839
  } = props;
24826
24840
  return /*#__PURE__*/React__namespace.createElement("div", {
24827
- className: aphrodite.css(styles$z.container)
24841
+ className: aphrodite.css(styles$C.container)
24828
24842
  }, controls && /*#__PURE__*/React__namespace.createElement("div", {
24829
- className: aphrodite.css(styles$z.columnLeft, styles$z.containerHeader)
24843
+ className: aphrodite.css(styles$C.columnLeft, styles$C.containerHeader)
24830
24844
  }, controls), /*#__PURE__*/React__namespace.createElement("div", null, children), /*#__PURE__*/React__namespace.createElement("div", {
24831
- className: aphrodite.css(styles$z.columnLeft)
24845
+ className: aphrodite.css(styles$C.columnLeft)
24832
24846
  }, /*#__PURE__*/React__namespace.createElement("a", {
24833
24847
  href: "javascript:void 0",
24834
24848
  onClick: () => actions.addArrayElement(path, shape.elementShape)
@@ -24842,28 +24856,28 @@ const ObjectContainer = _ref3 => {
24842
24856
  path
24843
24857
  } = _ref3;
24844
24858
  const headingEditor = /*#__PURE__*/React__namespace.createElement("div", {
24845
- className: aphrodite.css(styles$z.containerHeader)
24859
+ className: aphrodite.css(styles$C.containerHeader)
24846
24860
  }, /*#__PURE__*/React__namespace.createElement(Header, {
24847
24861
  depth: path.length,
24848
- className: aphrodite.css(styles$z.containerTitle)
24862
+ className: aphrodite.css(styles$C.containerTitle)
24849
24863
  }, capitalize(name)), controls);
24850
24864
  const headingPreview = (name || controls) && /*#__PURE__*/React__namespace.createElement("div", {
24851
- className: aphrodite.css(styles$z.containerHeader, styles$z.previewCollectionHeader)
24865
+ className: aphrodite.css(styles$C.containerHeader, styles$C.previewCollectionHeader)
24852
24866
  }, /*#__PURE__*/React__namespace.createElement(Header, {
24853
24867
  depth: path.length,
24854
- className: aphrodite.css(styles$z.containerTitle)
24868
+ className: aphrodite.css(styles$C.containerTitle)
24855
24869
  }, capitalize(name)));
24856
24870
  const hasBothHeadings = headingEditor && headingPreview;
24857
24871
  return /*#__PURE__*/React__namespace.createElement("div", {
24858
- className: aphrodite.css(styles$z.container)
24872
+ className: aphrodite.css(styles$C.container)
24859
24873
  }, hasBothHeadings && /*#__PURE__*/React__namespace.createElement("span", {
24860
- className: aphrodite.css(styles$z.row)
24874
+ className: aphrodite.css(styles$C.row)
24861
24875
  }, /*#__PURE__*/React__namespace.createElement("div", {
24862
- className: aphrodite.css(styles$z.columnLeft)
24876
+ className: aphrodite.css(styles$C.columnLeft)
24863
24877
  }, headingEditor), /*#__PURE__*/React__namespace.createElement("div", {
24864
- className: aphrodite.css(styles$z.columnRight)
24878
+ className: aphrodite.css(styles$C.columnRight)
24865
24879
  }, headingPreview)), /*#__PURE__*/React__namespace.createElement("div", {
24866
- className: aphrodite.css(path.length > 0 && styles$z.contentIndent)
24880
+ className: aphrodite.css(path.length > 0 && styles$C.contentIndent)
24867
24881
  }, children));
24868
24882
  };
24869
24883
 
@@ -24983,11 +24997,11 @@ const ItemNodeContent = withStickiness(props => {
24983
24997
  className: "framework-perseus"
24984
24998
  }, lens(renderers).get(path));
24985
24999
  return /*#__PURE__*/React__namespace.createElement("span", null, /*#__PURE__*/React__namespace.createElement("div", {
24986
- className: aphrodite.css(styles$z.row)
25000
+ className: aphrodite.css(styles$C.row)
24987
25001
  }, /*#__PURE__*/React__namespace.createElement("div", {
24988
- className: aphrodite.css(styles$z.columnLeft)
25002
+ className: aphrodite.css(styles$C.columnLeft)
24989
25003
  }, /*#__PURE__*/React__namespace.createElement("div", {
24990
- className: aphrodite.css(sticky && styles$z.sticky)
25004
+ className: aphrodite.css(sticky && styles$C.sticky)
24991
25005
  },
24992
25006
  /*#__PURE__*/
24993
25007
  // @ts-expect-error - TS2769 - No overload matches this call.
@@ -24995,9 +25009,9 @@ const ItemNodeContent = withStickiness(props => {
24995
25009
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
24996
25010
  apiOptions: apiOptions
24997
25011
  })))), /*#__PURE__*/React__namespace.createElement("div", {
24998
- className: aphrodite.css(styles$z.columnRight)
25012
+ className: aphrodite.css(styles$C.columnRight)
24999
25013
  }, /*#__PURE__*/React__namespace.createElement("div", {
25000
- className: aphrodite.css(sticky && styles$z.sticky)
25014
+ className: aphrodite.css(sticky && styles$C.sticky)
25001
25015
  }, preview))));
25002
25016
  });
25003
25017
  const HintNodeContent = withStickiness(props => {
@@ -25013,13 +25027,13 @@ const HintNodeContent = withStickiness(props => {
25013
25027
  className: "framework-perseus"
25014
25028
  }, lens(renderers).get(path));
25015
25029
  return /*#__PURE__*/React__namespace.createElement("div", {
25016
- className: aphrodite.css(styles$z.row)
25030
+ className: aphrodite.css(styles$C.row)
25017
25031
  }, /*#__PURE__*/React__namespace.createElement("div", {
25018
- className: aphrodite.css(styles$z.columnLeft)
25032
+ className: aphrodite.css(styles$C.columnLeft)
25019
25033
  }, /*#__PURE__*/React__namespace.createElement("div", {
25020
- className: aphrodite.css(sticky && styles$z.sticky)
25034
+ className: aphrodite.css(sticky && styles$C.sticky)
25021
25035
  }, /*#__PURE__*/React__namespace.createElement(HintEditor, _extends({}, data, {
25022
- className: aphrodite.css(styles$z.hintEditor),
25036
+ className: aphrodite.css(styles$C.hintEditor),
25023
25037
  onChange: newVal => actions.mergeValueAtPath(path, newVal),
25024
25038
  apiOptions: apiOptions,
25025
25039
  showTitle: false,
@@ -25034,9 +25048,9 @@ const HintNodeContent = withStickiness(props => {
25034
25048
  isFirst: true,
25035
25049
  isLast: true
25036
25050
  })))), /*#__PURE__*/React__namespace.createElement("div", {
25037
- className: aphrodite.css(styles$z.columnRight)
25051
+ className: aphrodite.css(styles$C.columnRight)
25038
25052
  }, /*#__PURE__*/React__namespace.createElement("div", {
25039
- className: aphrodite.css(sticky && styles$z.sticky)
25053
+ className: aphrodite.css(sticky && styles$C.sticky)
25040
25054
  }, preview)));
25041
25055
  });
25042
25056
  const TagsNodeContent = props => {
@@ -25053,9 +25067,9 @@ const TagsNodeContent = props => {
25053
25067
  return null;
25054
25068
  }
25055
25069
  return /*#__PURE__*/React__namespace.createElement("div", {
25056
- className: aphrodite.css(styles$z.columnLeft)
25070
+ className: aphrodite.css(styles$C.columnLeft)
25057
25071
  }, /*#__PURE__*/React__namespace.createElement("div", {
25058
- className: aphrodite.css(styles$z.tagsEditor)
25072
+ className: aphrodite.css(styles$C.tagsEditor)
25059
25073
  }, /*#__PURE__*/React__namespace.createElement(GroupMetadataEditor, {
25060
25074
  value: data,
25061
25075
  onChange: newVal => actions.setValueAtPath(path, newVal),
@@ -25086,23 +25100,23 @@ const ArrayNodeContent = props => {
25086
25100
  const subpath = path.concat(i);
25087
25101
  const controls = [i > 0 && /*#__PURE__*/React__namespace.createElement("div", {
25088
25102
  key: "moveArrayElementUp",
25089
- className: aphrodite.css(styles$z.control)
25103
+ className: aphrodite.css(styles$C.control)
25090
25104
  }, /*#__PURE__*/React__namespace.createElement(SimpleButton, {
25091
25105
  color: "orange",
25092
25106
  title: "Move up",
25093
25107
  onClick: () => actions.moveArrayElementUp(subpath)
25094
25108
  }, /*#__PURE__*/React__namespace.createElement("div", {
25095
- className: aphrodite.css(styles$z.verticalFlip)
25109
+ className: aphrodite.css(styles$C.verticalFlip)
25096
25110
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon$6, iconChevronDown)))), i < data.length - 1 && /*#__PURE__*/React__namespace.createElement("div", {
25097
25111
  key: "moveArrayElementDown",
25098
- className: aphrodite.css(styles$z.control)
25112
+ className: aphrodite.css(styles$C.control)
25099
25113
  }, /*#__PURE__*/React__namespace.createElement(SimpleButton, {
25100
25114
  color: "orange",
25101
25115
  title: "Move down",
25102
25116
  onClick: () => actions.moveArrayElementDown(subpath)
25103
25117
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon$6, iconChevronDown))), /*#__PURE__*/React__namespace.createElement("div", {
25104
25118
  key: "removeArrayElement",
25105
- className: aphrodite.css(styles$z.control)
25119
+ className: aphrodite.css(styles$C.control)
25106
25120
  }, /*#__PURE__*/React__namespace.createElement(SimpleButton, {
25107
25121
  color: "orange",
25108
25122
  title: "Delete",
@@ -25110,7 +25124,7 @@ const ArrayNodeContent = props => {
25110
25124
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon$6, iconTrash$4)))];
25111
25125
  return /*#__PURE__*/React__namespace.createElement("div", {
25112
25126
  key: i,
25113
- className: aphrodite.css(styles$z.arrayElement, !elementIsLeaf && styles$z.arrayElementAndNotLeaf)
25127
+ className: aphrodite.css(styles$C.arrayElement, !elementIsLeaf && styles$C.arrayElementAndNotLeaf)
25114
25128
  }, /*#__PURE__*/React__namespace.createElement(NodeContainer, _extends({}, otherProps, {
25115
25129
  key: i,
25116
25130
  shape: shape.elementShape,
@@ -25136,7 +25150,7 @@ const ObjectNodeContent = props => {
25136
25150
  // made sense to the shape's author is the order in which we'll iterate :)
25137
25151
  const children = Object.keys(shape.shape).map(subkey => /*#__PURE__*/React__namespace.createElement("div", {
25138
25152
  key: subkey,
25139
- className: aphrodite.css(styles$z.objectElement)
25153
+ className: aphrodite.css(styles$C.objectElement)
25140
25154
  }, /*#__PURE__*/React__namespace.createElement(NodeContainer, _extends({}, otherProps, {
25141
25155
  shape: shape.shape[subkey],
25142
25156
  data: data[subkey],
@@ -25306,7 +25320,7 @@ class MultiRendererEditor extends React__namespace.Component {
25306
25320
  }, this._renderContent());
25307
25321
  }
25308
25322
  }
25309
- const styles$z = aphrodite.StyleSheet.create({
25323
+ const styles$C = aphrodite.StyleSheet.create({
25310
25324
  // This is used in a number of places throughout this file.
25311
25325
  container: {},
25312
25326
  // eslint-disable-next-line react-native/no-unused-styles
@@ -25458,47 +25472,47 @@ const StatefulArticleEditor = createReactClass__default["default"]({
25458
25472
  screen
25459
25473
  } = this.state;
25460
25474
  return /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("div", {
25461
- style: styles$y.controlBar
25475
+ style: styles$B.controlBar
25462
25476
  }, /*#__PURE__*/React__namespace.createElement("span", {
25463
- style: styles$y.controls
25477
+ style: styles$B.controls
25464
25478
  }, "Mode:", " ", /*#__PURE__*/React__namespace.createElement("span", {
25465
25479
  onClick: () => this.setState({
25466
25480
  mode: "edit"
25467
25481
  }),
25468
- style: mode === "edit" ? styles$y.controlSelected : styles$y.control
25482
+ style: mode === "edit" ? styles$B.controlSelected : styles$B.control
25469
25483
  }, "EDIT"), " | ", /*#__PURE__*/React__namespace.createElement("span", {
25470
25484
  onClick: () => this.setState({
25471
25485
  mode: "preview"
25472
25486
  }),
25473
- style: mode === "preview" ? styles$y.controlSelected : styles$y.control
25487
+ style: mode === "preview" ? styles$B.controlSelected : styles$B.control
25474
25488
  }, "PREVIEW"), " | ", /*#__PURE__*/React__namespace.createElement("span", {
25475
25489
  onClick: () => this.setState({
25476
25490
  mode: "json"
25477
25491
  }),
25478
- style: mode === "json" ? styles$y.controlSelected : styles$y.control
25492
+ style: mode === "json" ? styles$B.controlSelected : styles$B.control
25479
25493
  }, "JSON")), /*#__PURE__*/React__namespace.createElement("span", {
25480
- style: styles$y.controls
25494
+ style: styles$B.controls
25481
25495
  }, "Screen:", " ", /*#__PURE__*/React__namespace.createElement("span", {
25482
25496
  onClick: () => this.setState({
25483
25497
  screen: "phone"
25484
25498
  }),
25485
- style: screen === "phone" ? styles$y.controlSelected : styles$y.control
25499
+ style: screen === "phone" ? styles$B.controlSelected : styles$B.control
25486
25500
  }, "PHONE"), " | ", /*#__PURE__*/React__namespace.createElement("span", {
25487
25501
  onClick: () => this.setState({
25488
25502
  screen: "tablet"
25489
25503
  }),
25490
- style: screen === "tablet" ? styles$y.controlSelected : styles$y.control
25504
+ style: screen === "tablet" ? styles$B.controlSelected : styles$B.control
25491
25505
  }, "TABLET"), " | ", /*#__PURE__*/React__namespace.createElement("span", {
25492
25506
  onClick: () => this.setState({
25493
25507
  screen: "desktop"
25494
25508
  }),
25495
- style: screen === "desktop" ? styles$y.controlSelected : styles$y.control
25509
+ style: screen === "desktop" ? styles$B.controlSelected : styles$B.control
25496
25510
  }, "DESKTOP"))), /*#__PURE__*/React__namespace.createElement("div", {
25497
- style: styles$y.editor
25511
+ style: styles$B.editor
25498
25512
  }, /*#__PURE__*/React__namespace.createElement(ArticleEditor, this.state)));
25499
25513
  }
25500
25514
  });
25501
- const styles$y = {
25515
+ const styles$B = {
25502
25516
  controlBar: {
25503
25517
  marginBottom: 25,
25504
25518
  marginTop: 5
@@ -25713,7 +25727,7 @@ class BlurInput extends React__namespace.Component {
25713
25727
 
25714
25728
  /* eslint-disable no-useless-escape */
25715
25729
  const {
25716
- InfoTip: InfoTip$l,
25730
+ InfoTip: InfoTip$k,
25717
25731
  PropCheckBox: PropCheckBox$b
25718
25732
  } = perseus.components;
25719
25733
  const DEFAULT_WIDTH = 400;
@@ -25901,7 +25915,7 @@ class CSProgramEditor extends React__namespace.Component {
25901
25915
  label: "Show Editor",
25902
25916
  showEditor: this.props.showEditor,
25903
25917
  onChange: this.props.onChange
25904
- }), /*#__PURE__*/React__namespace.createElement(InfoTip$l, null, "If you show the editor, you should use the \"full-width\" alignment to make room for the width of the editor."), /*#__PURE__*/React__namespace.createElement("br", null), /*#__PURE__*/React__namespace.createElement(PropCheckBox$b, {
25918
+ }), /*#__PURE__*/React__namespace.createElement(InfoTip$k, null, "If you show the editor, you should use the \"full-width\" alignment to make room for the width of the editor."), /*#__PURE__*/React__namespace.createElement("br", null), /*#__PURE__*/React__namespace.createElement(PropCheckBox$b, {
25905
25919
  label: "Show Buttons",
25906
25920
  showButtons: this.props.showButtons,
25907
25921
  onChange: this.props.onChange
@@ -25909,7 +25923,7 @@ class CSProgramEditor extends React__namespace.Component {
25909
25923
  name: "settings",
25910
25924
  pairs: this.props.settings,
25911
25925
  onChange: this._handleSettingsChange
25912
- }), /*#__PURE__*/React__namespace.createElement(InfoTip$l, null, "Settings that you add here are available to the program as an object returned by ", /*#__PURE__*/React__namespace.createElement("code", null, "Program.settings()"))));
25926
+ }), /*#__PURE__*/React__namespace.createElement(InfoTip$k, null, "Settings that you add here are available to the program as an object returned by ", /*#__PURE__*/React__namespace.createElement("code", null, "Program.settings()"))));
25913
25927
  }
25914
25928
  }
25915
25929
 
@@ -25986,7 +26000,7 @@ class DeprecatedStandinEditor extends React__namespace.Component {
25986
26000
 
25987
26001
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
25988
26002
  const {
25989
- InfoTip: InfoTip$k,
26003
+ InfoTip: InfoTip$j,
25990
26004
  InlineIcon: InlineIcon$5
25991
26005
  } = perseus.components;
25992
26006
  const {
@@ -26067,14 +26081,14 @@ class DropdownEditor extends React__namespace.Component {
26067
26081
  className: "perseus-widget-dropdown"
26068
26082
  }, /*#__PURE__*/React__namespace.createElement("div", {
26069
26083
  className: "dropdown-info"
26070
- }, "Dropdown", /*#__PURE__*/React__namespace.createElement(InfoTip$k, null, /*#__PURE__*/React__namespace.createElement("p", null, "The drop down is useful for making inequalities in a custom format. We normally use the symbols ", "<", ",", " ", ">", ", \u2264, \u2265 (in that order) which you can copy into the choices. When possible, use the \"multiple choice\" answer type instead."))), /*#__PURE__*/React__namespace.createElement("div", {
26084
+ }, "Dropdown", /*#__PURE__*/React__namespace.createElement(InfoTip$j, null, /*#__PURE__*/React__namespace.createElement("p", null, "The drop down is useful for making inequalities in a custom format. We normally use the symbols ", "<", ",", " ", ">", ", \u2264, \u2265 (in that order) which you can copy into the choices. When possible, use the \"multiple choice\" answer type instead."))), /*#__PURE__*/React__namespace.createElement("div", {
26071
26085
  className: "dropdown-placeholder"
26072
26086
  }, /*#__PURE__*/React__namespace.createElement("input", {
26073
26087
  type: "text",
26074
26088
  placeholder: "Placeholder value",
26075
26089
  value: this.props.placeholder,
26076
26090
  onChange: this.onPlaceholderChange
26077
- }), /*#__PURE__*/React__namespace.createElement(InfoTip$k, null, /*#__PURE__*/React__namespace.createElement("p", null, "This value will appear as the drop down default. It should give the user some indication of the values available in the drop down itself, e.g., Yes/No/Maybe."))), /*#__PURE__*/React__namespace.createElement("div", {
26091
+ }), /*#__PURE__*/React__namespace.createElement(InfoTip$j, null, /*#__PURE__*/React__namespace.createElement("p", null, "This value will appear as the drop down default. It should give the user some indication of the values available in the drop down itself, e.g., Yes/No/Maybe."))), /*#__PURE__*/React__namespace.createElement("div", {
26078
26092
  className: "clearfix"
26079
26093
  }), /*#__PURE__*/React__namespace.createElement("ul", {
26080
26094
  className: "dropdown-choices"
@@ -26608,7 +26622,7 @@ const SortableItem = createReactClass__default["default"]({
26608
26622
  });
26609
26623
 
26610
26624
  const {
26611
- InfoTip: InfoTip$j,
26625
+ InfoTip: InfoTip$i,
26612
26626
  PropCheckBox: PropCheckBox$a
26613
26627
  } = perseus.components;
26614
26628
 
@@ -26734,11 +26748,11 @@ class ExpressionEditor extends React__namespace.Component {
26734
26748
  onChange: this.props.onChange,
26735
26749
  labelAlignment: "right",
26736
26750
  label: "Use \xD7 for rendering multiplication instead of a center dot."
26737
- }), /*#__PURE__*/React__namespace.createElement(InfoTip$j, null, /*#__PURE__*/React__namespace.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__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, "Function variables: ", /*#__PURE__*/React__namespace.createElement("input", {
26751
+ }), /*#__PURE__*/React__namespace.createElement(InfoTip$i, null, /*#__PURE__*/React__namespace.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__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, "Function variables: ", /*#__PURE__*/React__namespace.createElement("input", {
26738
26752
  type: "text",
26739
26753
  defaultValue: this.props.functions.join(" "),
26740
26754
  onChange: this.handleFunctions
26741
- })), /*#__PURE__*/React__namespace.createElement(InfoTip$j, null, /*#__PURE__*/React__namespace.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__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("div", null, "Button sets:"), buttonSetChoices), /*#__PURE__*/React__namespace.createElement("h3", {
26755
+ })), /*#__PURE__*/React__namespace.createElement(InfoTip$i, null, /*#__PURE__*/React__namespace.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__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("div", null, "Button sets:"), buttonSetChoices), /*#__PURE__*/React__namespace.createElement("h3", {
26742
26756
  className: "expression-editor-h3"
26743
26757
  }, "Answers"), /*#__PURE__*/React__namespace.createElement("p", {
26744
26758
  style: {
@@ -26952,14 +26966,14 @@ class AnswerOption extends React__namespace.Component {
26952
26966
  onChange: this.props.onChange,
26953
26967
  labelAlignment: "right",
26954
26968
  label: "Answer expression must have the same form."
26955
- }), /*#__PURE__*/React__namespace.createElement(InfoTip$j, null, /*#__PURE__*/React__namespace.createElement("p", null, "The student's answer must be in the same form. Commutativity and excess negative signs are ignored."))), /*#__PURE__*/React__namespace.createElement("div", {
26969
+ }), /*#__PURE__*/React__namespace.createElement(InfoTip$i, null, /*#__PURE__*/React__namespace.createElement("p", null, "The student's answer must be in the same form. Commutativity and excess negative signs are ignored."))), /*#__PURE__*/React__namespace.createElement("div", {
26956
26970
  className: "answer-option"
26957
26971
  }, /*#__PURE__*/React__namespace.createElement(PropCheckBox$a, {
26958
26972
  simplify: this.props.simplify,
26959
26973
  onChange: this.props.onChange,
26960
26974
  labelAlignment: "right",
26961
26975
  label: "Answer expression must be fully expanded and simplified."
26962
- }), /*#__PURE__*/React__namespace.createElement(InfoTip$j, null, /*#__PURE__*/React__namespace.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__namespace.createElement("div", {
26976
+ }), /*#__PURE__*/React__namespace.createElement(InfoTip$i, null, /*#__PURE__*/React__namespace.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__namespace.createElement("div", {
26963
26977
  className: "remove-container"
26964
26978
  }, removeButton)));
26965
26979
  }
@@ -27037,10 +27051,10 @@ class GradedGroupEditor extends React__namespace.Component {
27037
27051
  }, /*#__PURE__*/React__namespace.createElement("div", {
27038
27052
  className: "perseus-widget-row"
27039
27053
  }, /*#__PURE__*/React__namespace.createElement("label", {
27040
- className: aphrodite.css(styles$x.title)
27054
+ className: aphrodite.css(styles$A.title)
27041
27055
  }, "Title:", " ", /*#__PURE__*/React__namespace.createElement(TextInput$6, {
27042
27056
  value: this.props.title,
27043
- className: aphrodite.css(styles$x.input)
27057
+ className: aphrodite.css(styles$A.input)
27044
27058
  // @ts-expect-error - TS2554 - Expected 3 arguments, but got 1.
27045
27059
  ,
27046
27060
  onChange: this.change("title")
@@ -27065,7 +27079,7 @@ class GradedGroupEditor extends React__namespace.Component {
27065
27079
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon$4, iconPlus$1), " Add a hint"), this.props.hint && /*#__PURE__*/React__namespace.createElement("div", {
27066
27080
  className: "perseus-hint-editor"
27067
27081
  }, /*#__PURE__*/React__namespace.createElement("div", {
27068
- className: aphrodite.css(styles$x.hintsTitle)
27082
+ className: aphrodite.css(styles$A.hintsTitle)
27069
27083
  }, "Hint"), /*#__PURE__*/React__namespace.createElement(Editor, {
27070
27084
  ref: this.hintEditor,
27071
27085
  content: this.props.hint ? this.props.hint.content : "",
@@ -27097,7 +27111,7 @@ class GradedGroupEditor extends React__namespace.Component {
27097
27111
  };
27098
27112
  };
27099
27113
  }
27100
- const styles$x = aphrodite.StyleSheet.create({
27114
+ const styles$A = aphrodite.StyleSheet.create({
27101
27115
  title: {
27102
27116
  fontSize: 18,
27103
27117
  fontWeight: "bold"
@@ -27184,7 +27198,7 @@ const setArrayItem = (list, i, value) => [...list.slice(0, i), value, ...list.sl
27184
27198
  /* eslint-disable react/forbid-prop-types, react/no-unsafe */
27185
27199
  const {
27186
27200
  ButtonGroup: ButtonGroup$4,
27187
- InfoTip: InfoTip$i,
27201
+ InfoTip: InfoTip$h,
27188
27202
  PropCheckBox: PropCheckBox$9,
27189
27203
  RangeInput: RangeInput$6
27190
27204
  } = perseus.components;
@@ -27645,7 +27659,7 @@ const GraphSettings = createReactClass__default["default"]({
27645
27659
  },
27646
27660
  onKeyPress: this.changeBackgroundUrl,
27647
27661
  onBlur: this.changeBackgroundUrl
27648
- }), /*#__PURE__*/React__namespace.createElement(InfoTip$i, null, /*#__PURE__*/React__namespace.createElement("p", null, "Create an image in graphie, or use the \"Add image\" function to create a background.")))), ___default["default"].contains(this.props.editableSettings, "measure") && /*#__PURE__*/React__namespace.createElement("div", {
27662
+ }), /*#__PURE__*/React__namespace.createElement(InfoTip$h, null, /*#__PURE__*/React__namespace.createElement("p", null, "Create an image in graphie, or use the \"Add image\" function to create a background.")))), ___default["default"].contains(this.props.editableSettings, "measure") && /*#__PURE__*/React__namespace.createElement("div", {
27649
27663
  className: "misc-settings"
27650
27664
  }, /*#__PURE__*/React__namespace.createElement("div", {
27651
27665
  className: "perseus-widget-row"
@@ -27683,7 +27697,7 @@ const GraphSettings = createReactClass__default["default"]({
27683
27697
  });
27684
27698
 
27685
27699
  const {
27686
- InfoTip: InfoTip$h,
27700
+ InfoTip: InfoTip$g,
27687
27701
  MultiButtonGroup: MultiButtonGroup$1
27688
27702
  } = perseus.components;
27689
27703
  const {
@@ -27757,7 +27771,7 @@ class GrapherEditor extends React__namespace.Component {
27757
27771
  className: "perseus-error"
27758
27772
  }, this.props.graph.valid);
27759
27773
  }
27760
- return /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("div", null, "Correct answer", " ", /*#__PURE__*/React__namespace.createElement(InfoTip$h, null, /*#__PURE__*/React__namespace.createElement("p", null, "Graph the correct answer in the graph below and ensure the equation or point coordinates displayed represent the correct answer.")), " ", ": ", equationString), /*#__PURE__*/React__namespace.createElement(GraphSettings, {
27774
+ return /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("div", null, "Correct answer", " ", /*#__PURE__*/React__namespace.createElement(InfoTip$g, null, /*#__PURE__*/React__namespace.createElement("p", null, "Graph the correct answer in the graph below and ensure the equation or point coordinates displayed represent the correct answer.")), " ", ": ", equationString), /*#__PURE__*/React__namespace.createElement(GraphSettings, {
27761
27775
  editableSettings: ["graph", "snap", "image"],
27762
27776
  box: getInteractiveBoxFromSizeClass$1(sizeClass),
27763
27777
  range: this.props.graph.range,
@@ -28032,7 +28046,7 @@ class IframeEditor extends React__namespace.Component {
28032
28046
 
28033
28047
  /* eslint-disable jsx-a11y/anchor-is-valid, react/sort-comp */
28034
28048
  const {
28035
- InfoTip: InfoTip$g,
28049
+ InfoTip: InfoTip$f,
28036
28050
  InlineIcon: InlineIcon$3,
28037
28051
  RangeInput: RangeInput$5
28038
28052
  } = perseus.components;
@@ -28089,7 +28103,7 @@ class ImageEditor extends React__namespace.Component {
28089
28103
  style: {
28090
28104
  width: "100%"
28091
28105
  }
28092
- }))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, /*#__PURE__*/React__namespace.createElement("div", null, "Dimensions:"), /*#__PURE__*/React__namespace.createElement("p", null, backgroundImage.width, "x", backgroundImage.height))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, /*#__PURE__*/React__namespace.createElement("div", null, "Alt text:", /*#__PURE__*/React__namespace.createElement(InfoTip$g, null, "This is important for screenreaders. The content of this alt text will be formatted as markdown (tables, emphasis, etc. are supported).")), /*#__PURE__*/React__namespace.createElement(Editor, {
28106
+ }))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, /*#__PURE__*/React__namespace.createElement("div", null, "Dimensions:"), /*#__PURE__*/React__namespace.createElement("p", null, backgroundImage.width, "x", backgroundImage.height))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, /*#__PURE__*/React__namespace.createElement("div", null, "Alt text:", /*#__PURE__*/React__namespace.createElement(InfoTip$f, null, "This is important for screenreaders. The content of this alt text will be formatted as markdown (tables, emphasis, etc. are supported).")), /*#__PURE__*/React__namespace.createElement(Editor, {
28093
28107
  apiOptions: this.props.apiOptions,
28094
28108
  content: this.props.alt,
28095
28109
  onChange: props => {
@@ -28113,7 +28127,7 @@ class ImageEditor extends React__namespace.Component {
28113
28127
  }, this.state.backgroundImageError);
28114
28128
  return /*#__PURE__*/React__namespace.createElement("div", {
28115
28129
  className: "perseus-image-editor"
28116
- }, /*#__PURE__*/React__namespace.createElement("label", null, "Image url:", /*#__PURE__*/React__namespace.createElement(InfoTip$g, null, "Paste an image or graphie image URL."), this.state.backgroundImageError && backgroundImageErrorText, /*#__PURE__*/React__namespace.createElement(BlurInput, {
28130
+ }, /*#__PURE__*/React__namespace.createElement("label", null, "Image url:", /*#__PURE__*/React__namespace.createElement(InfoTip$f, null, "Paste an image or graphie image URL."), this.state.backgroundImageError && backgroundImageErrorText, /*#__PURE__*/React__namespace.createElement(BlurInput, {
28117
28131
  value: backgroundImage.url || "",
28118
28132
  style: {
28119
28133
  width: 332
@@ -28269,7 +28283,7 @@ class ImageEditor extends React__namespace.Component {
28269
28283
 
28270
28284
  /* eslint-disable react/sort-comp */
28271
28285
  const {
28272
- InfoTip: InfoTip$f
28286
+ InfoTip: InfoTip$e
28273
28287
  } = perseus.components;
28274
28288
  const answerTypes = {
28275
28289
  number: {
@@ -28349,7 +28363,7 @@ class InputNumberEditor extends React__namespace.Component {
28349
28363
  value: "optional"
28350
28364
  }, "will be accepted"), /*#__PURE__*/React__namespace.createElement("option", {
28351
28365
  value: "enforced"
28352
- }, "will be marked wrong"))), /*#__PURE__*/React__namespace.createElement(InfoTip$f, null, /*#__PURE__*/React__namespace.createElement("p", null, "Normally select \"will not be graded\". This will give the user a message saying the answer is correct but not simplified. The user will then have to simplify it and re-enter, but will not be penalized. (5th grade and anything after)"), /*#__PURE__*/React__namespace.createElement("p", null, "Select \"will be accepted\" only if the user is not expected to know how to simplify fractions yet. (Anything prior to 5th grade)"), /*#__PURE__*/React__namespace.createElement("p", null, "Select \"will be marked wrong\" only if we are specifically assessing the ability to simplify."))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, /*#__PURE__*/React__namespace.createElement("input", {
28366
+ }, "will be marked wrong"))), /*#__PURE__*/React__namespace.createElement(InfoTip$e, null, /*#__PURE__*/React__namespace.createElement("p", null, "Normally select \"will not be graded\". This will give the user a message saying the answer is correct but not simplified. The user will then have to simplify it and re-enter, but will not be penalized. (5th grade and anything after)"), /*#__PURE__*/React__namespace.createElement("p", null, "Select \"will be accepted\" only if the user is not expected to know how to simplify fractions yet. (Anything prior to 5th grade)"), /*#__PURE__*/React__namespace.createElement("p", null, "Select \"will be marked wrong\" only if we are specifically assessing the ability to simplify."))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, /*#__PURE__*/React__namespace.createElement("input", {
28353
28367
  type: "checkbox",
28354
28368
  checked: this.props.inexact,
28355
28369
  onChange: e => {
@@ -28385,7 +28399,7 @@ class InputNumberEditor extends React__namespace.Component {
28385
28399
  });
28386
28400
  },
28387
28401
  "aria-label": "Answer type"
28388
- }, answerTypeOptions), /*#__PURE__*/React__namespace.createElement(InfoTip$f, null, /*#__PURE__*/React__namespace.createElement("p", null, "Use the default \"Numbers\" unless the answer must be in a specific form (e.g., question is about converting decimals to fractions)."))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, "Width", " ", /*#__PURE__*/React__namespace.createElement("select", {
28402
+ }, answerTypeOptions), /*#__PURE__*/React__namespace.createElement(InfoTip$e, null, /*#__PURE__*/React__namespace.createElement("p", null, "Use the default \"Numbers\" unless the answer must be in a specific form (e.g., question is about converting decimals to fractions)."))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, "Width", " ", /*#__PURE__*/React__namespace.createElement("select", {
28389
28403
  value: this.props.size,
28390
28404
  onChange: e => {
28391
28405
  // @ts-expect-error - TS2322 - Type 'string' is not assignable to type '"small" | "normal" | undefined'.
@@ -28397,7 +28411,7 @@ class InputNumberEditor extends React__namespace.Component {
28397
28411
  value: "normal"
28398
28412
  }, "Normal (80px)"), /*#__PURE__*/React__namespace.createElement("option", {
28399
28413
  value: "small"
28400
- }, "Small (40px)"))), /*#__PURE__*/React__namespace.createElement(InfoTip$f, null, /*#__PURE__*/React__namespace.createElement("p", null, "Use size \"Normal\" for all text boxes, unless there are multiple text boxes in one line and the answer area is too narrow to fit them."))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, /*#__PURE__*/React__namespace.createElement("input", {
28414
+ }, "Small (40px)"))), /*#__PURE__*/React__namespace.createElement(InfoTip$e, null, /*#__PURE__*/React__namespace.createElement("p", null, "Use size \"Normal\" for all text boxes, unless there are multiple text boxes in one line and the answer area is too narrow to fit them."))), /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("label", null, /*#__PURE__*/React__namespace.createElement("input", {
28401
28415
  type: "checkbox",
28402
28416
  checked: this.props.rightAlign,
28403
28417
  onChange: e => {
@@ -28468,13 +28482,13 @@ function MathquillInput(props) {
28468
28482
  }
28469
28483
  });
28470
28484
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
28471
- style: styles$w.outerWrapper
28485
+ style: styles$z.outerWrapper
28472
28486
  }, /*#__PURE__*/React__namespace.createElement("span", {
28473
28487
  ref: mathFieldWrapperRef,
28474
28488
  className: "perseus-math-input mq-editable-field mq-math-mode"
28475
28489
  }));
28476
28490
  }
28477
- const styles$w = aphrodite.StyleSheet.create({
28491
+ const styles$z = aphrodite.StyleSheet.create({
28478
28492
  outerWrapper: {
28479
28493
  display: "inline-block",
28480
28494
  borderStyle: "solid",
@@ -29535,96 +29549,6 @@ class InteractionEditor extends React__namespace.Component {
29535
29549
  }
29536
29550
  }
29537
29551
 
29538
- const UNLIMITED = "unlimited";
29539
- // Parses a string (ostensibly from some UI element that only supports string
29540
- // types, such as a <select />) into a numeric point count or the special sigil
29541
- // "unlimited". Note that 0 is a special case, and means "unlimited".
29542
- const parsePointCount = points => {
29543
- const parsed = parseInt(points, 10);
29544
- if (isNaN(parsed)) {
29545
- return UNLIMITED;
29546
- }
29547
- return parsed === 0 ? UNLIMITED : parsed;
29548
- };
29549
-
29550
- const GraphPointsCountSelector = _ref => {
29551
- let {
29552
- numPoints = 1,
29553
- onChange
29554
- } = _ref;
29555
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
29556
- selectedValue: `${numPoints}`,
29557
- onChange: newValue => {
29558
- onChange(parsePointCount(newValue));
29559
- }
29560
- // Never uses placeholder, always has value
29561
- ,
29562
- placeholder: "",
29563
- style: styles$v.singleSelectShort
29564
- }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29565
- key: n,
29566
- value: `${n}`,
29567
- label: `${n} point${n > 1 ? "s" : ""}`
29568
- })), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29569
- key: "unlimited",
29570
- value: UNLIMITED,
29571
- label: "unlimited"
29572
- })]);
29573
- };
29574
- const styles$v = aphrodite.StyleSheet.create({
29575
- singleSelectShort: {
29576
- // Non-standard spacing, but it's the smallest we can go
29577
- // without running into styling issues with the dropdown.
29578
- height: 26
29579
- }
29580
- });
29581
-
29582
- const GraphTypeSelector = props => {
29583
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
29584
- selectedValue: props.graphType,
29585
- onChange: props.onChange,
29586
- placeholder: "Select a graph type",
29587
- style: styles$u.singleSelectShort
29588
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29589
- value: "linear",
29590
- label: "Linear function"
29591
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29592
- value: "quadratic",
29593
- label: "Quadratic function"
29594
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29595
- value: "sinusoid",
29596
- label: "Sinusoid function"
29597
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29598
- value: "circle",
29599
- label: "Circle"
29600
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29601
- value: "point",
29602
- label: "Point(s)"
29603
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29604
- value: "linear-system",
29605
- label: "Linear System"
29606
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29607
- value: "polygon",
29608
- label: "Polygon"
29609
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29610
- value: "segment",
29611
- label: "Line Segment(s)"
29612
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29613
- value: "ray",
29614
- label: "Ray"
29615
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29616
- value: "angle",
29617
- label: "Angle"
29618
- }));
29619
- };
29620
- const styles$u = aphrodite.StyleSheet.create({
29621
- singleSelectShort: {
29622
- // Non-standard spacing, but it's the smallest we can go
29623
- // without running into styling issues with the dropdown.
29624
- height: 26
29625
- }
29626
- });
29627
-
29628
29552
  // Renders the given children in a View laid out horizontally.
29629
29553
  const LabeledRow = props => {
29630
29554
  const {
@@ -29634,16 +29558,16 @@ const LabeledRow = props => {
29634
29558
  style
29635
29559
  } = props;
29636
29560
  return /*#__PURE__*/React__namespace.createElement("label", {
29637
- className: aphrodite.css(styles$t.label)
29561
+ className: aphrodite.css(styles$y.label)
29638
29562
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
29639
- style: [styles$t.row, style]
29563
+ style: [styles$y.row, style]
29640
29564
  }, labelSide === "start" || /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, {
29641
- style: styles$t.spaceEnd
29565
+ style: styles$y.spaceEnd
29642
29566
  }, label), children, labelSide === "end" && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, {
29643
- style: styles$t.spaceStart
29567
+ style: styles$y.spaceStart
29644
29568
  }, label)));
29645
29569
  };
29646
- const styles$t = aphrodite.StyleSheet.create({
29570
+ const styles$y = aphrodite.StyleSheet.create({
29647
29571
  label: {
29648
29572
  width: "fit-content"
29649
29573
  },
@@ -29661,903 +29585,1044 @@ const styles$t = aphrodite.StyleSheet.create({
29661
29585
  }
29662
29586
  });
29663
29587
 
29664
- /* eslint-disable react/forbid-prop-types, react/no-unsafe */
29665
- const {
29666
- ButtonGroup: ButtonGroup$2,
29667
- InfoTip: InfoTip$e,
29668
- PropCheckBox: PropCheckBox$7,
29669
- RangeInput: RangeInput$4
29670
- } = perseus.components;
29671
- const defaultBackgroundImage$1 = {
29672
- url: null,
29673
- width: 0,
29674
- height: 0
29588
+ const mediaDefaultSpecLargeMarginWidth = wonderBlocksTokens.spacing.large_24;
29589
+ const MEDIA_DEFAULT_SPEC = {
29590
+ small: {
29591
+ query: "(max-width: 767px)",
29592
+ totalColumns: 4,
29593
+ gutterWidth: wonderBlocksTokens.spacing.medium_16,
29594
+ marginWidth: wonderBlocksTokens.spacing.medium_16
29595
+ },
29596
+ medium: {
29597
+ query: "(min-width: 768px) and (max-width: 1023px)",
29598
+ totalColumns: 8,
29599
+ gutterWidth: wonderBlocksTokens.spacing.xLarge_32,
29600
+ marginWidth: wonderBlocksTokens.spacing.large_24
29601
+ },
29602
+ large: {
29603
+ query: "(min-width: 1024px)",
29604
+ totalColumns: 12,
29605
+ gutterWidth: wonderBlocksTokens.spacing.xLarge_32,
29606
+ marginWidth: mediaDefaultSpecLargeMarginWidth,
29607
+ maxWidth: 1120 + mediaDefaultSpecLargeMarginWidth * 2
29608
+ }
29675
29609
  };
29676
- function numSteps(range, step) {
29677
- return Math.floor((range[1] - range[0]) / step);
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
- }
29704
- class InteractiveGraphSettings extends React__namespace.Component {
29705
- _isMounted = false;
29706
- bgUrlRef = /*#__PURE__*/React__namespace.createRef();
29707
- labelXRef = /*#__PURE__*/React__namespace.createRef();
29708
- labelYRef = /*#__PURE__*/React__namespace.createRef();
29709
- static stateFromProps(props) {
29710
- return {
29711
- labelsTextbox: props.labels,
29712
- gridStepTextbox: props.gridStep,
29713
- snapStepTextbox: props.snapStep,
29714
- stepTextbox: props.step,
29715
- rangeTextbox: props.range,
29716
- // Shallow-copied clone
29717
- backgroundImage: {
29718
- ...props.backgroundImage
29719
- }
29720
- };
29610
+ ({
29611
+ large: {
29612
+ query: "(min-width: 1px)",
29613
+ totalColumns: 12,
29614
+ gutterWidth: wonderBlocksTokens.spacing.xLarge_32,
29615
+ marginWidth: wonderBlocksTokens.spacing.medium_16
29721
29616
  }
29722
- constructor(props) {
29723
- super(props);
29724
- this.state = {
29725
- isExpanded: true,
29726
- ...InteractiveGraphSettings.stateFromProps(props)
29727
- };
29617
+ });
29618
+ ({
29619
+ small: {
29620
+ query: "(max-width: 767px)",
29621
+ totalColumns: 4,
29622
+ gutterWidth: wonderBlocksTokens.spacing.medium_16,
29623
+ marginWidth: wonderBlocksTokens.spacing.medium_16
29624
+ },
29625
+ large: {
29626
+ query: "(min-width: 768px)",
29627
+ totalColumns: 12,
29628
+ gutterWidth: wonderBlocksTokens.spacing.xLarge_32,
29629
+ marginWidth: wonderBlocksTokens.spacing.xxLarge_48
29728
29630
  }
29729
- static defaultProps = {
29730
- box: [perseus.interactiveSizes.defaultBoxSizeSmall, perseus.interactiveSizes.defaultBoxSizeSmall],
29731
- labels: ["x", "y"],
29732
- range: [[-10, 10], [-10, 10]],
29733
- step: [1, 1],
29734
- gridStep: [1, 1],
29735
- snapStep: [1, 1],
29736
- valid: true,
29737
- backgroundImage: defaultBackgroundImage$1,
29738
- markings: "graph",
29739
- showProtractor: false,
29740
- showRuler: false,
29741
- showTooltips: false,
29742
- rulerLabel: "",
29743
- rulerTicks: 10
29744
- };
29745
- componentDidMount() {
29746
- // TODO(scottgrant): This is a hack to remove the deprecated call to
29747
- // this.isMounted() but is still considered an anti-pattern.
29748
- this._isMounted = true;
29749
- this.changeGraph = ___default["default"].debounce(this.changeGraph, 300);
29631
+ });
29632
+
29633
+ const defaultContext = {
29634
+ ssrSize: "large",
29635
+ mediaSpec: MEDIA_DEFAULT_SPEC
29636
+ };
29637
+ const MediaLayoutContext = React__namespace.createContext(defaultContext);
29638
+ MediaLayoutContext.displayName = "MediaLayoutContext";
29639
+
29640
+ class Spring extends React__namespace.Component {
29641
+ render() {
29642
+ const {
29643
+ style
29644
+ } = this.props;
29645
+ return React__namespace.createElement(wonderBlocksCore.View, {
29646
+ "aria-hidden": "true",
29647
+ style: [styles$x.grow, style]
29648
+ });
29750
29649
  }
29751
- UNSAFE_componentWillReceiveProps(nextProps) {
29752
- // Make sure that state updates when switching
29753
- // between different items in a multi-item editor.
29754
- if (!___default["default"].isEqual(this.props.labels, nextProps.labels) || !___default["default"].isEqual(this.props.gridStep, nextProps.gridStep) || !___default["default"].isEqual(this.props.snapStep, nextProps.snapStep) || !___default["default"].isEqual(this.props.step, nextProps.step) || !___default["default"].isEqual(this.props.range, nextProps.range) || !___default["default"].isEqual(this.props.backgroundImage, nextProps.backgroundImage)) {
29755
- this.setState(InteractiveGraphSettings.stateFromProps(nextProps));
29756
- }
29757
- }
29758
- componentWillUnmount() {
29759
- this._isMounted = false;
29650
+ }
29651
+ const styles$x = aphrodite.StyleSheet.create({
29652
+ grow: {
29653
+ flexGrow: 1
29760
29654
  }
29761
- change = (() => {
29762
- var _this = this;
29763
- return function () {
29764
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
29765
- args[_key] = arguments[_key];
29766
- }
29767
- return perseus.Changeable.change.apply(_this, args);
29768
- };
29769
- })();
29655
+ });
29770
29656
 
29771
- // TODO(aria): Make either a wrapper for standard events to work
29772
- // with this.change, or make these use some TextInput/NumberInput box
29773
- changeRulerLabel = e => {
29774
- this.change({
29775
- rulerLabel: e.target.value
29776
- });
29777
- };
29778
- changeRulerTicks = e => {
29779
- this.change({
29780
- rulerTicks: +e.target.value
29781
- });
29782
- };
29783
- changeBackgroundUrl = e => {
29784
- // Only continue on blur or "enter"
29785
- if (e.type === "keypress" && e.key !== "Enter") {
29786
- return;
29787
- }
29788
- const setUrl = (url, width, height) => {
29789
- // Shallow-copied clone
29790
- const image = {
29791
- ...this.props.backgroundImage
29792
- };
29793
- image.url = url;
29794
- image.width = width;
29795
- image.height = height;
29796
- this.setState({
29797
- backgroundImage: image
29798
- }, this.changeGraph);
29799
- };
29800
- const url = this.bgUrlRef.current?.value;
29801
- if (url) {
29802
- perseus.Util.getImageSize(url, (width, height) => {
29803
- if (this._isMounted) {
29804
- setUrl(url, width, height);
29805
- }
29806
- });
29807
- } else {
29808
- setUrl(null, 0, 0);
29809
- }
29810
- };
29811
- renderLabelChoices = choices => {
29812
- return choices.map(nameAndValue => /*#__PURE__*/React__namespace.createElement("option", {
29813
- key: nameAndValue[1],
29814
- value: nameAndValue[1]
29815
- }, nameAndValue[0]));
29816
- };
29817
- validRange = range => {
29818
- const numbers = ___default["default"].every(range, function (num) {
29819
- return ___default["default"].isFinite(num);
29820
- });
29821
- if (!numbers) {
29822
- return "Range must be a valid number";
29823
- }
29824
- if (range[0] >= range[1]) {
29825
- return "Range must have a higher number on the right";
29826
- }
29827
- return true;
29828
- };
29829
- validateStepValue = settings => {
29657
+ class Strut extends React__namespace.Component {
29658
+ render() {
29830
29659
  const {
29831
- step,
29832
- range,
29833
- name,
29834
- minTicks,
29835
- maxTicks
29836
- } = settings;
29837
- const nSteps = numSteps(range, step);
29838
- if (nSteps < minTicks) {
29839
- return name + " is too large, there must be at least " + minTicks + " ticks.";
29840
- }
29841
- if (nSteps > maxTicks) {
29842
- return name + " is too small, there can be at most " + maxTicks + " ticks.";
29843
- }
29844
- return true;
29845
- };
29846
- validSnapStep = (step, range) => {
29847
- return this.validateStepValue({
29848
- step: step,
29849
- range: range,
29850
- name: "Snap step",
29851
- minTicks: 5,
29852
- maxTicks: 60
29853
- });
29854
- };
29855
- validGridStep = (step, range) => {
29856
- return this.validateStepValue({
29857
- step: step,
29858
- range: range,
29859
- name: "Grid step",
29860
- minTicks: 3,
29861
- maxTicks: 60
29862
- });
29863
- };
29864
- validStep = (step, range) => {
29865
- return this.validateStepValue({
29866
- step: step,
29867
- range: range,
29868
- name: "Step",
29869
- minTicks: 3,
29870
- maxTicks: 20
29871
- });
29872
- };
29873
- validBackgroundImageSize = image => {
29874
- // Ignore empty images
29875
- if (!image.url) {
29876
- return true;
29877
- }
29878
- const validSize = image.width <= 450 && image.height <= 450;
29879
- if (!validSize) {
29880
- return "Image must be smaller than 450px x 450px.";
29881
- }
29882
- return true;
29883
- };
29884
- validateGraphSettings = (range, step, gridStep, snapStep, image) => {
29885
- const self = this;
29886
- let msg;
29887
- const goodRange = ___default["default"].every(range, function (range) {
29888
- msg = self.validRange(range);
29889
- return msg === true;
29890
- });
29891
- if (!goodRange) {
29892
- return msg;
29893
- }
29894
- const goodStep = ___default["default"].every(step, function (step, i) {
29895
- msg = self.validStep(step, range[i]);
29896
- return msg === true;
29897
- });
29898
- if (!goodStep) {
29899
- return msg;
29900
- }
29901
- const goodGridStep = ___default["default"].every(gridStep, function (gridStep, i) {
29902
- msg = self.validGridStep(gridStep, range[i]);
29903
- return msg === true;
29904
- });
29905
- if (!goodGridStep) {
29906
- return msg;
29907
- }
29908
- const goodSnapStep = ___default["default"].every(snapStep, function (snapStep, i) {
29909
- msg = self.validSnapStep(snapStep, range[i]);
29910
- return msg === true;
29660
+ size,
29661
+ style
29662
+ } = this.props;
29663
+ return React__namespace.createElement(wonderBlocksCore.View, {
29664
+ "aria-hidden": "true",
29665
+ style: [strutStyle(size), style]
29911
29666
  });
29912
- if (!goodSnapStep) {
29913
- return msg;
29914
- }
29915
- const goodImageSize = this.validBackgroundImageSize(image);
29916
- if (goodImageSize !== true) {
29917
- msg = goodImageSize;
29918
- return msg;
29919
- }
29920
- return true;
29921
- };
29922
- changeLabel = (i, e) => {
29923
- const val = e.target.value;
29924
- const labels = this.state.labelsTextbox.slice();
29925
- labels[i] = val;
29926
- this.setState({
29927
- labelsTextbox: labels
29928
- }, this.changeGraph);
29929
- };
29930
- changeRange = (i, values) => {
29931
- const ranges = this.state.rangeTextbox.slice();
29932
- ranges[i] = values;
29933
- const step = this.state.stepTextbox.slice();
29934
- const gridStep = this.state.gridStepTextbox.slice();
29935
- const snapStep = this.state.snapStepTextbox.slice();
29936
- const scale = perseus.Util.scaleFromExtent(ranges[i], this.props.box[i]);
29937
- if (this.validRange(ranges[i]) === true) {
29938
- step[i] = perseus.Util.tickStepFromExtent(ranges[i], this.props.box[i]);
29939
- const gridStepValue = perseus.Util.gridStepFromTickStep(step[i], scale);
29940
- if (gridStepValue) {
29941
- gridStep[i] = gridStepValue;
29942
- }
29943
- snapStep[i] = gridStep[i] / 2;
29944
- }
29945
- this.setState({
29946
- stepTextbox: step,
29947
- gridStepTextbox: gridStep,
29948
- snapStepTextbox: snapStep,
29949
- rangeTextbox: ranges
29950
- }, this.changeGraph);
29951
- };
29952
- changeStep = step => {
29953
- this.setState({
29954
- stepTextbox: step
29955
- }, this.changeGraph);
29956
- };
29957
- changeSnapStep = snapStep => {
29958
- this.setState({
29959
- snapStepTextbox: snapStep
29960
- }, this.changeGraph);
29961
- };
29962
- changeGridStep = gridStep => {
29963
- this.setState({
29964
- gridStepTextbox: gridStep,
29965
- snapStepTextbox: ___default["default"].map(gridStep, function (step) {
29966
- return step / 2;
29967
- })
29968
- }, this.changeGraph);
29667
+ }
29668
+ }
29669
+ const strutStyle = size => {
29670
+ return {
29671
+ width: size,
29672
+ MsFlexBasis: size,
29673
+ MsFlexPreferredSize: size,
29674
+ WebkitFlexBasis: size,
29675
+ flexBasis: size,
29676
+ flexShrink: 0
29969
29677
  };
29970
- changeGraph = () => {
29971
- const labels = this.state.labelsTextbox;
29972
- const range = ___default["default"].map(this.state.rangeTextbox, function (range) {
29973
- return ___default["default"].map(range, Number);
29974
- });
29975
- const step = ___default["default"].map(this.state.stepTextbox, Number);
29976
- const gridStep = this.state.gridStepTextbox;
29977
- const snapStep = this.state.snapStepTextbox;
29978
- const image = this.state.backgroundImage;
29678
+ };
29979
29679
 
29980
- // validationResult is either:
29981
- // true -> the settings are valid
29982
- // a string -> the settings are invalid, and the explanation
29983
- // is contained in the string
29984
- // TODO(aria): Refactor this to not be confusing
29985
- const validationResult = this.validateGraphSettings(range, step, gridStep, snapStep, image);
29986
- if (validationResult === true) {
29987
- // either true or a string
29988
- this.change({
29989
- valid: true,
29990
- labels: labels,
29991
- range: range,
29992
- step: step,
29993
- gridStep: gridStep,
29994
- snapStep: snapStep,
29995
- backgroundImage: image
29996
- });
29997
- } else {
29998
- this.change({
29999
- valid: validationResult // a string message, not false
30000
- });
29680
+ function focusWithChromeStickyFocusBugWorkaround(element) {
29681
+ // NOTE(marksandstrom) Chrome sticky focus bug.
29682
+ //
29683
+ // There is a bug in Chrome related to sticky positioning and
29684
+ // focus: When a child of an element with sticky positioning is
29685
+ // focused with the focus() method, the scrollable area
29686
+ // containing that element will scroll to the top (even though
29687
+ // the element is already visble on the page). For a dropdown
29688
+ // this means that the area will be scrolled to the top when
29689
+ // the user interacts with the dropdown (since we actively
29690
+ // manage dropdown header and option focus for keyboard users).
29691
+ //
29692
+ // To work around this bug the `preventScroll` focus option is
29693
+ // used.
29694
+ //
29695
+ // Using `focusOptions` is an experimental browser feature
29696
+ // "that should not be used in production code". However, since
29697
+ // Chrome is the only browser we support that exhibits this
29698
+ // buggy behavior and using `focusOptions` works around the
29699
+ // bug, it is perfectly reasonable to use the feature in this
29700
+ // case. We are not relying on the effect of the
29701
+ // `preventScroll` option otherwise.
29702
+ //
29703
+ // focusOptions documentation:
29704
+ //
29705
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
29706
+ //
29707
+ // Note that there could be cases where we want default focus() behavior.
29708
+ // One that comes to mind is a dropdown with a long list of options that
29709
+ // might be rendered off screen when opened. With this workaround the
29710
+ // selected option would not be scrolled into view if the option is near
29711
+ // the end of the list. We're not aware of any uses of the dropdown where
29712
+ // this is an issue, however, so there isn't currently a way to out of the
29713
+ // `preventScroll` behavior.
29714
+
29715
+ // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element'.
29716
+ element.focus({
29717
+ preventScroll: true
29718
+ });
29719
+ }
29720
+ const DEFAULT_COLOR = "grayH";
29721
+ function getDefaultFigureForType(type) {
29722
+ switch (type) {
29723
+ case "point":
29724
+ return {
29725
+ type: "point",
29726
+ coord: [0, 0],
29727
+ color: DEFAULT_COLOR,
29728
+ filled: true
29729
+ };
29730
+ case "line":
29731
+ return {
29732
+ type: "line",
29733
+ kind: "line",
29734
+ points: [getDefaultFigureForType("point"), {
29735
+ ...getDefaultFigureForType("point"),
29736
+ coord: [2, 2]
29737
+ }],
29738
+ color: DEFAULT_COLOR,
29739
+ lineStyle: "solid",
29740
+ showPoint1: false,
29741
+ showPoint2: false
29742
+ };
29743
+ case "vector":
29744
+ return {
29745
+ type: "vector",
29746
+ points: [[0, 0], [2, 2]],
29747
+ color: DEFAULT_COLOR
29748
+ };
29749
+ case "ellipse":
29750
+ return {
29751
+ type: "ellipse",
29752
+ center: [0, 0],
29753
+ radius: [1, 1],
29754
+ angle: 0,
29755
+ color: DEFAULT_COLOR,
29756
+ fillStyle: "none",
29757
+ strokeStyle: "solid"
29758
+ };
29759
+ case "polygon":
29760
+ return {
29761
+ type: "polygon",
29762
+ points: [[0, 2], [-1, 0], [1, 0]],
29763
+ color: DEFAULT_COLOR,
29764
+ showVertices: false,
29765
+ fillStyle: "none",
29766
+ strokeStyle: "solid"
29767
+ };
29768
+ default:
29769
+ throw new wonderStuffCore.UnreachableCaseError(type);
29770
+ }
29771
+ }
29772
+ function degreeToRadian(degrees) {
29773
+ return degrees / 180 * Math.PI;
29774
+ }
29775
+ function radianToDegree(radians) {
29776
+ return radians / Math.PI * 180;
29777
+ }
29778
+
29779
+ /**
29780
+ * Dropdown for selecting a locked figure to add to an interactive graph.
29781
+ * Locked figures are elements (points, segmeents, etc.) that are not
29782
+ * interactive, just present in the graph's background.
29783
+ *
29784
+ * Used in the interactive graph editor's locked figures section.
29785
+ */
29786
+ const LockedFigureSelect = props => {
29787
+ const {
29788
+ id,
29789
+ onChange
29790
+ } = props;
29791
+ const figureTypes = props.showM2Features ? ["point", "line", "vector", "ellipse", "polygon"] : ["point", "line"];
29792
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
29793
+ style: styles$w.container
29794
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionMenu, {
29795
+ menuText: "Add locked figure",
29796
+ style: styles$w.addElementSelect
29797
+ }, figureTypes.map(figureType => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionItem, {
29798
+ key: `${id}-${figureType}`,
29799
+ label: figureType,
29800
+ onClick: () => onChange(figureType)
29801
+ }, figureType))));
29802
+ };
29803
+ const styles$w = aphrodite.StyleSheet.create({
29804
+ container: {
29805
+ marginTop: wonderBlocksTokens.spacing.xSmall_8
29806
+ },
29807
+ addElementSelect: {
29808
+ backgroundColor: wonderBlocksTokens.color.fadedBlue8,
29809
+ borderRadius: wonderBlocksTokens.spacing.xxxSmall_4
29810
+ }
29811
+ });
29812
+
29813
+ const AngleInput = props => {
29814
+ const {
29815
+ angle,
29816
+ onChange
29817
+ } = props;
29818
+ const [angleInput, setAngleInput] = React__namespace.useState(radianToDegree(angle).toString());
29819
+ function handleAngleChange(newValue) {
29820
+ // Update the local state (update the input field value).
29821
+ setAngleInput(newValue);
29822
+
29823
+ // If the new value is not a number, don't update the props.
29824
+ // If it's empty, keep the props the same value instead of setting to 0.
29825
+ if (isNaN(+newValue) || newValue === "") {
29826
+ return;
30001
29827
  }
30002
- };
30003
29828
 
30004
- render() {
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", {
30012
- className: "graph-settings"
30013
- }, /*#__PURE__*/React__namespace.createElement("div", {
30014
- className: "perseus-widget-row"
30015
- }, /*#__PURE__*/React__namespace.createElement("div", {
30016
- className: "perseus-widget-left-col"
30017
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30018
- label: "x Label"
30019
- }, /*#__PURE__*/React__namespace.createElement("input", {
30020
- type: "text",
30021
- className: "graph-settings-axis-label",
30022
- ref: this.labelXRef,
30023
- onChange: e => this.changeLabel(0, e),
30024
- value: this.state.labelsTextbox[0] || ""
30025
- }))), /*#__PURE__*/React__namespace.createElement("div", {
30026
- className: "perseus-widget-right-col"
30027
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30028
- label: "y Label"
30029
- }, /*#__PURE__*/React__namespace.createElement("input", {
30030
- type: "text",
30031
- className: "graph-settings-axis-label",
30032
- ref: this.labelYRef,
30033
- onChange: e => this.changeLabel(1, e),
30034
- value: this.state.labelsTextbox[1] || ""
30035
- })))), /*#__PURE__*/React__namespace.createElement("div", {
30036
- className: "perseus-widget-row"
30037
- }, /*#__PURE__*/React__namespace.createElement("div", {
30038
- className: "perseus-widget-left-col"
30039
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30040
- label: "x Range"
30041
- }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
30042
- value: this.state.rangeTextbox[0],
30043
- onChange: vals => this.changeRange(0, vals)
30044
- }))), /*#__PURE__*/React__namespace.createElement("div", {
30045
- className: "perseus-widget-right-col"
30046
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30047
- label: "y Range"
30048
- }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
30049
- value: this.state.rangeTextbox[1],
30050
- onChange: vals => this.changeRange(1, vals)
30051
- })))), /*#__PURE__*/React__namespace.createElement("div", {
30052
- className: "perseus-widget-row"
30053
- }, /*#__PURE__*/React__namespace.createElement("div", {
30054
- className: "perseus-widget-left-col"
30055
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30056
- label: "Tick Step"
30057
- }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
30058
- value: this.state.stepTextbox,
30059
- onChange: this.changeStep
30060
- }))), /*#__PURE__*/React__namespace.createElement("div", {
30061
- className: "perseus-widget-right-col"
30062
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30063
- label: "Grid Step"
30064
- }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
30065
- value: this.state.gridStepTextbox,
30066
- onChange: this.changeGridStep
30067
- })))), /*#__PURE__*/React__namespace.createElement("div", {
30068
- className: "perseus-widget-row"
30069
- }, /*#__PURE__*/React__namespace.createElement("div", {
30070
- className: "perseus-widget-left-col"
30071
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30072
- label: "Snap Step"
30073
- }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
30074
- value: this.state.snapStepTextbox,
30075
- onChange: this.changeSnapStep
30076
- })))), /*#__PURE__*/React__namespace.createElement("div", {
30077
- className: "perseus-widget-row"
30078
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30079
- label: "Markings:"
30080
- }, /*#__PURE__*/React__namespace.createElement(ButtonGroup$2, {
30081
- value: this.props.markings,
30082
- allowEmpty: false,
30083
- buttons: [{
30084
- value: "graph",
30085
- content: "Graph"
30086
- }, {
30087
- value: "grid",
30088
- content: "Grid"
30089
- }, {
30090
- value: "none",
30091
- content: "None"
30092
- }],
30093
- onChange: this.change("markings")
30094
- }))), /*#__PURE__*/React__namespace.createElement("div", {
30095
- className: "perseus-widget-left-col"
30096
- }, /*#__PURE__*/React__namespace.createElement(PropCheckBox$7, {
30097
- label: "Show tooltips",
30098
- showTooltips: this.props.showTooltips,
30099
- onChange: this.change
30100
- }))), /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30101
- label: "Background image URL:",
30102
- style: styles$s.resetSpaceTop
30103
- }, /*#__PURE__*/React__namespace.createElement("input", {
30104
- type: "text",
30105
- className: aphrodite.css(styles$s.backgroundUrlInput),
30106
- ref: this.bgUrlRef,
30107
- value: this.state.backgroundImage.url || "",
30108
- onChange: e => {
30109
- // Shallow-copied clone
30110
- const image = {
30111
- ...this.props.backgroundImage
30112
- };
30113
- image.url = e.target.value;
30114
- this.setState({
30115
- backgroundImage: image
30116
- });
30117
- },
30118
- onKeyPress: this.changeBackgroundUrl,
30119
- onBlur: this.changeBackgroundUrl
30120
- }), /*#__PURE__*/React__namespace.createElement(InfoTip$e, 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, {
30121
- style: styles$s.rulerSection
30122
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30123
- style: styles$s.checkboxRow
30124
- }, /*#__PURE__*/React__namespace.createElement(PropCheckBox$7, {
30125
- label: "Show ruler",
30126
- showRuler: this.props.showRuler,
30127
- onChange: this.change,
30128
- style: styles$s.resetSpaceTop
30129
- }), /*#__PURE__*/React__namespace.createElement(PropCheckBox$7, {
30130
- label: "Show protractor",
30131
- showProtractor: this.props.showProtractor,
30132
- onChange: this.change,
30133
- style: styles$s.resetSpaceTop
30134
- })), (this.props.showRuler || this.props.showProtractor) && /*#__PURE__*/React__namespace.createElement(Banner__default["default"], {
30135
- layout: "floating",
30136
- text: "The ruler and protractor are not accessible. Please consider an alternate approach.",
30137
- kind: "warning"
30138
- }), this.props.showRuler && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30139
- style: styles$s.spaceTop
30140
- }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30141
- label: "Ruler label:",
30142
- style: styles$s.resetSpaceTop
30143
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30144
- id: "ruler-label-select",
30145
- selectedValue: this.props.rulerLabel,
30146
- onChange: newValue => {
30147
- this.change({
30148
- rulerLabel: newValue
30149
- });
30150
- },
30151
- placeholder: "None",
30152
- style: styles$s.singleSelectShort
30153
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30154
- value: "",
30155
- label: "None",
30156
- horizontalRule: "full-width"
30157
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30158
- value: "mm",
30159
- label: "Milimeters"
30160
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30161
- value: "cm",
30162
- label: "Centimeters"
30163
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30164
- value: "m",
30165
- label: "Meters"
30166
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30167
- value: "km",
30168
- label: "Kilometers",
30169
- horizontalRule: "full-width"
30170
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30171
- value: "in",
30172
- label: "Inches"
30173
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30174
- value: "ft",
30175
- label: "Feet"
30176
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30177
- value: "yd",
30178
- label: "Yards"
30179
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30180
- value: "mi",
30181
- label: "Miles"
30182
- }))), /*#__PURE__*/React__namespace.createElement(LabeledRow, {
30183
- label: "Ruler ticks:"
30184
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30185
- id: "ruler-ticks-select",
30186
- selectedValue: `${this.props.rulerTicks}`,
30187
- onChange: newValue => {
30188
- this.change({
30189
- rulerTicks: newValue
30190
- });
30191
- },
30192
- placeholder: "10",
30193
- style: styles$s.singleSelectShort
30194
- }, [1, 2, 4, 8, 10, 16].map(value => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30195
- key: value,
30196
- value: `${value}`,
30197
- label: `${value}`
30198
- }))))))));
29829
+ // Update the graph.
29830
+ onChange(degreeToRadian(newValue));
29831
+ }
29832
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
29833
+ tag: "label",
29834
+ style: styles$v.row
29835
+ }, "angle (degrees)", /*#__PURE__*/React__namespace.createElement(Strut, {
29836
+ size: wonderBlocksTokens.spacing.xxSmall_6
29837
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
29838
+ type: "number",
29839
+ value: angleInput,
29840
+ onChange: handleAngleChange,
29841
+ style: styles$v.textField
29842
+ }), /*#__PURE__*/React__namespace.createElement(Strut, {
29843
+ size: wonderBlocksTokens.spacing.xxSmall_6
29844
+ }));
29845
+ };
29846
+ const styles$v = aphrodite.StyleSheet.create({
29847
+ row: {
29848
+ display: "flex",
29849
+ flexDirection: "row",
29850
+ alignItems: "center"
29851
+ },
29852
+ textField: {
29853
+ width: wonderBlocksTokens.spacing.xxxLarge_64
29854
+ }
29855
+ });
29856
+
29857
+ const ColorSwatch = props => {
29858
+ const {
29859
+ color,
29860
+ filled = true,
29861
+ decorative = false
29862
+ } = props;
29863
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
29864
+ "aria-label":
29865
+ // Don't include an aria label for decorative circles.
29866
+ !decorative ? `${color}, ${filled ? "filled" : "open"}` : undefined,
29867
+ style: [styles$u.colorSwatch, {
29868
+ border: `4px solid ${perseus.lockedFigureColors[color]}`,
29869
+ backgroundColor: filled ? perseus.lockedFigureColors[color] : wonderBlocksTokens.color.white
29870
+ }]
29871
+ });
29872
+ };
29873
+ const styles$u = aphrodite.StyleSheet.create({
29874
+ colorSwatch: {
29875
+ // Add a white outline so that the color swatch is visible when
29876
+ // the dropdown option is highlighted with its blue background.
29877
+ outline: `2px solid ${wonderBlocksTokens.color.offWhite}`,
29878
+ borderRadius: "50%",
29879
+ width: wonderBlocksTokens.spacing.large_24,
29880
+ height: wonderBlocksTokens.spacing.large_24
29881
+ }
29882
+ });
29883
+
29884
+ const possibleColors = Object.keys(perseus.lockedFigureColors);
29885
+ const ColorSelect = props => {
29886
+ const {
29887
+ selectedValue,
29888
+ style,
29889
+ onChange
29890
+ } = props;
29891
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
29892
+ style: [styles$t.row, style]
29893
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
29894
+ tag: "label",
29895
+ style: styles$t.row
29896
+ }, "color", /*#__PURE__*/React__namespace.createElement(Strut, {
29897
+ size: wonderBlocksTokens.spacing.xxSmall_6
29898
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
29899
+ selectedValue: selectedValue,
29900
+ onChange: onChange
29901
+ // Placeholder is required, but never gets used.
29902
+ ,
29903
+ placeholder: ""
29904
+ }, possibleColors.map(colorName => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
29905
+ key: colorName,
29906
+ value: colorName,
29907
+ label: colorName,
29908
+ leftAccessory: /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
29909
+ color: colorName,
29910
+ decorative: true
29911
+ })
29912
+ }, colorName)))));
29913
+ };
29914
+ const styles$t = aphrodite.StyleSheet.create({
29915
+ row: {
29916
+ display: "flex",
29917
+ flexDirection: "row",
29918
+ alignItems: "center"
29919
+ }
29920
+ });
29921
+
29922
+ const CoordinatePairInput = props => {
29923
+ const {
29924
+ coord,
29925
+ labels,
29926
+ error,
29927
+ style,
29928
+ onChange
29929
+ } = props;
29930
+
29931
+ // Keep track of the coordinates via state as the user is editing them,
29932
+ // before they are updated in the props as a valid number.
29933
+ const [coordState, setCoordState] = React__namespace.useState([
29934
+ // Using strings to make it easier to work with the text fields.
29935
+ coord[0].toString(), coord[1].toString()]);
29936
+ function handleCoordChange(newValue, coordIndex) {
29937
+ // Update the local state (update the input field value).
29938
+ const newCoordState = [...coordState];
29939
+ newCoordState[coordIndex] = newValue;
29940
+ setCoordState(newCoordState);
29941
+
29942
+ // If the new value is not a number, don't update the props.
29943
+ // If it's empty, keep the props the same value instead of setting to 0.
29944
+ if (isNaN(+newValue) || newValue === "") {
29945
+ return;
29946
+ }
29947
+
29948
+ // Update the props (update the graph).
29949
+ const newCoords = [...coord];
29950
+ newCoords[coordIndex] = +newValue;
29951
+ onChange(newCoords);
30199
29952
  }
30200
- }
29953
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
29954
+ style: [styles$s.row, style]
29955
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
29956
+ tag: "label",
29957
+ style: styles$s.row
29958
+ }, labels ? labels[0] : "x coord", /*#__PURE__*/React__namespace.createElement(Strut, {
29959
+ size: wonderBlocksTokens.spacing.xxSmall_6
29960
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
29961
+ type: "number",
29962
+ value: coordState[0],
29963
+ onChange: newValue => handleCoordChange(newValue, 0),
29964
+ style: [styles$s.textField, error ? styles$s.errorField : undefined]
29965
+ })), /*#__PURE__*/React__namespace.createElement(Strut, {
29966
+ size: wonderBlocksTokens.spacing.medium_16
29967
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
29968
+ tag: "label",
29969
+ style: styles$s.row
29970
+ }, labels ? labels[1] : "y coord", /*#__PURE__*/React__namespace.createElement(Strut, {
29971
+ size: wonderBlocksTokens.spacing.xxSmall_6
29972
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
29973
+ type: "number",
29974
+ value: coordState[1],
29975
+ onChange: newValue => handleCoordChange(newValue, 1),
29976
+ style: [styles$s.textField, error ? styles$s.errorField : undefined]
29977
+ })));
29978
+ };
30201
29979
  const styles$s = aphrodite.StyleSheet.create({
30202
- resetSpaceTop: {
30203
- marginTop: 0
29980
+ row: {
29981
+ display: "flex",
29982
+ flexDirection: "row",
29983
+ alignItems: "center"
30204
29984
  },
30205
- spaceTop: {
30206
- marginTop: wonderBlocksTokens.spacing.xSmall_8
29985
+ textField: {
29986
+ width: wonderBlocksTokens.spacing.xxxLarge_64
30207
29987
  },
30208
- singleSelectShort: {
30209
- // Non-standard spacing, but it's the smallest we can go
30210
- // without running into styling issues with the dropdown.
30211
- height: 26
29988
+ errorField: {
29989
+ borderColor: wonderBlocksTokens.color.red,
29990
+ backgroundColor: wonderBlocksTokens.color.fadedRed8
29991
+ }
29992
+ });
29993
+
29994
+ const EllipseSwatch = props => {
29995
+ const {
29996
+ color,
29997
+ fillStyle,
29998
+ strokeStyle
29999
+ } = props;
30000
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30001
+ "aria-label": `${color}, stroke ${strokeStyle}, fill ${fillStyle}`,
30002
+ style: [styles$r.container, {
30003
+ border: `4px ${strokeStyle} ${perseus.lockedFigureColors[color]}`
30004
+ }]
30005
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30006
+ style: [styles$r.innerCircle, {
30007
+ backgroundColor: perseus.lockedFigureColors[color],
30008
+ opacity: perseus.lockedFigureFillStyles[fillStyle]
30009
+ }]
30010
+ }));
30011
+ };
30012
+ const styles$r = aphrodite.StyleSheet.create({
30013
+ container: {
30014
+ // Add a white outline so that the color swatch is visible when
30015
+ // the dropdown option is highlighted with its blue background.
30016
+ outline: `2px solid ${wonderBlocksTokens.color.offWhite}`,
30017
+ borderRadius: "50%",
30018
+ width: wonderBlocksTokens.spacing.xLarge_32,
30019
+ height: wonderBlocksTokens.spacing.large_24,
30020
+ backgroundColor: wonderBlocksTokens.color.white,
30021
+ alignItems: "center",
30022
+ justifyContent: "center"
30212
30023
  },
30213
- backgroundUrlInput: {
30214
- border: `1px solid ${wonderBlocksTokens.color.offBlack32}`,
30215
- borderRadius: wonderBlocksTokens.spacing.xxxSmall_4,
30216
- padding: wonderBlocksTokens.spacing.xxxSmall_4
30024
+ innerCircle: {
30025
+ width: 28,
30026
+ height: 20,
30027
+ borderRadius: "50%"
30028
+ }
30029
+ });
30030
+
30031
+ const LockedFigureSettingsAccordion = props => {
30032
+ const {
30033
+ children,
30034
+ header,
30035
+ expanded,
30036
+ containerStyle,
30037
+ panelStyle,
30038
+ onToggle
30039
+ } = props;
30040
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View
30041
+ // More specificity so that we can override the default
30042
+ // .heading > h2 > .header styles from the articles.less
30043
+ // file (which is imported in perseus-renderer.less).
30044
+ , {
30045
+ className: "locked-figure-accordion"
30046
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksAccordion.AccordionSection, {
30047
+ expanded: expanded,
30048
+ onToggle: onToggle,
30049
+ style: [styles$q.container, containerStyle],
30050
+ headerStyle: styles$q.accordionHeader,
30051
+ header: header
30052
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30053
+ style: [styles$q.accordionPanel, panelStyle]
30054
+ }, children)));
30055
+ };
30056
+ const styles$q = aphrodite.StyleSheet.create({
30057
+ container: {
30058
+ backgroundColor: wonderBlocksTokens.color.fadedBlue8,
30059
+ marginTop: wonderBlocksTokens.spacing.xSmall_8
30217
30060
  },
30218
- checkboxRow: {
30219
- flexDirection: "row",
30220
- alignItems: "center",
30221
- justifyContent: "space-between",
30222
- marginBottom: wonderBlocksTokens.spacing.xSmall_8
30061
+ accordionHeader: {
30062
+ padding: wonderBlocksTokens.spacing.small_12,
30063
+ // Don't move the dropdown caret.
30064
+ paddingInlineEnd: 0,
30065
+ // Fixed height so the addition of the color swatch doesn't
30066
+ // change the height of the header when toggling.
30067
+ height: wonderBlocksTokens.spacing.xxLarge_48
30223
30068
  },
30224
- rulerSection: {
30225
- marginTop: wonderBlocksTokens.spacing.xSmall_8,
30226
- borderTop: `1px solid ${wonderBlocksTokens.color.offBlack16}`,
30227
- paddingTop: wonderBlocksTokens.spacing.xSmall_8,
30228
- paddingBottom: wonderBlocksTokens.spacing.xSmall_8,
30229
- borderBottom: `1px solid ${wonderBlocksTokens.color.offBlack16}`
30069
+ accordionPanel: {
30070
+ paddingTop: wonderBlocksTokens.spacing.xxSmall_6,
30071
+ paddingBottom: wonderBlocksTokens.spacing.xxxSmall_4,
30072
+ paddingLeft: wonderBlocksTokens.spacing.small_12,
30073
+ paddingRight: wonderBlocksTokens.spacing.small_12
30230
30074
  }
30231
30075
  });
30232
30076
 
30233
- const mediaDefaultSpecLargeMarginWidth = wonderBlocksTokens.spacing.large_24;
30234
- const MEDIA_DEFAULT_SPEC = {
30235
- small: {
30236
- query: "(max-width: 767px)",
30237
- totalColumns: 4,
30238
- gutterWidth: wonderBlocksTokens.spacing.medium_16,
30239
- marginWidth: wonderBlocksTokens.spacing.medium_16
30077
+ /**
30078
+ * The part of a locked figure settings panel that contains actions
30079
+ * for this panel and its associated locked figure, including
30080
+ * the delete button.
30081
+ */
30082
+ const LockedFigureSettingsActions = props => {
30083
+ const {
30084
+ figureType,
30085
+ onMove,
30086
+ onRemove
30087
+ } = props;
30088
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30089
+ style: styles$p.container
30090
+ }, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
30091
+ startIcon: trashIcon__default["default"],
30092
+ "aria-label": `Delete locked ${figureType}`,
30093
+ onClick: onRemove,
30094
+ kind: "tertiary",
30095
+ style: styles$p.deleteButton
30096
+ }, "Delete"), /*#__PURE__*/React__namespace.createElement(Spring, null), props.showM2Features && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
30097
+ icon: caretDoubleUpIcon__default["default"],
30098
+ size: "small",
30099
+ "aria-label": `Move locked ${figureType} to the back`,
30100
+ onClick: () => onMove("back"),
30101
+ style: styles$p.iconButton
30102
+ }), /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
30103
+ icon: caretUpIcon__default["default"],
30104
+ size: "small",
30105
+ "aria-label": `Move locked ${figureType} backward`,
30106
+ onClick: () => onMove("backward"),
30107
+ style: styles$p.iconButton
30108
+ }), /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
30109
+ icon: caretDownIcon__default["default"],
30110
+ size: "small",
30111
+ "aria-label": `Move locked ${figureType} forward`,
30112
+ onClick: () => onMove("forward"),
30113
+ style: styles$p.iconButton
30114
+ }), /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
30115
+ icon: caretDoubleDownIcon__default["default"],
30116
+ size: "small",
30117
+ "aria-label": `Move locked ${figureType} to the front`,
30118
+ onClick: () => onMove("front"),
30119
+ style: styles$p.iconButton
30120
+ })));
30121
+ };
30122
+ const styles$p = aphrodite.StyleSheet.create({
30123
+ container: {
30124
+ width: "100%",
30125
+ flexDirection: "row",
30126
+ alignItems: "center",
30127
+ marginTop: wonderBlocksTokens.spacing.xxxSmall_4
30240
30128
  },
30241
- medium: {
30242
- query: "(min-width: 768px) and (max-width: 1023px)",
30243
- totalColumns: 8,
30244
- gutterWidth: wonderBlocksTokens.spacing.xLarge_32,
30245
- marginWidth: wonderBlocksTokens.spacing.large_24
30129
+ deleteButton: {
30130
+ // Line up the delete icon with the rest of the content.
30131
+ marginInlineStart: -wonderBlocksTokens.spacing.xxxSmall_4
30246
30132
  },
30247
- large: {
30248
- query: "(min-width: 1024px)",
30249
- totalColumns: 12,
30250
- gutterWidth: wonderBlocksTokens.spacing.xLarge_32,
30251
- marginWidth: mediaDefaultSpecLargeMarginWidth,
30252
- maxWidth: 1120 + mediaDefaultSpecLargeMarginWidth * 2
30133
+ iconButton: {
30134
+ // Reset the margin because the icon buttons
30135
+ // overlap each other otherwise.
30136
+ margin: 0
30137
+ }
30138
+ });
30139
+
30140
+ const {
30141
+ InfoTip: InfoTip$d
30142
+ } = perseus.components;
30143
+ const LockedEllipseSettings = props => {
30144
+ const {
30145
+ center,
30146
+ radius,
30147
+ angle,
30148
+ color,
30149
+ fillStyle,
30150
+ strokeStyle,
30151
+ expanded,
30152
+ onToggle,
30153
+ onChangeProps,
30154
+ onMove,
30155
+ onRemove
30156
+ } = props;
30157
+ function handleColorChange(newValue) {
30158
+ onChangeProps({
30159
+ color: newValue
30160
+ });
30253
30161
  }
30162
+ return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30163
+ expanded: expanded,
30164
+ onToggle: onToggle,
30165
+ header:
30166
+ /*#__PURE__*/
30167
+ // Summary: Ellipse, center, radius, color (opacity, dashed)
30168
+ React__namespace.createElement(wonderBlocksCore.View, {
30169
+ style: styles$o.row
30170
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Ellipse (${center[0]}, ${center[1]}), radius ${radius[0]}, ${radius[1]}`), /*#__PURE__*/React__namespace.createElement(Strut, {
30171
+ size: wonderBlocksTokens.spacing.xSmall_8
30172
+ }), /*#__PURE__*/React__namespace.createElement(EllipseSwatch, {
30173
+ color: props.color,
30174
+ fillStyle: fillStyle,
30175
+ strokeStyle: strokeStyle
30176
+ }))
30177
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30178
+ style: styles$o.row
30179
+ }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30180
+ coord: center,
30181
+ style: styles$o.spaceUnder,
30182
+ onChange: newCoords => onChangeProps({
30183
+ center: newCoords
30184
+ })
30185
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30186
+ style: styles$o.spaceUnder
30187
+ }, /*#__PURE__*/React__namespace.createElement(InfoTip$d, null, "The coordinates for the center of the ellipse."))), /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30188
+ coord: radius,
30189
+ labels: ["x radius", "y radius"],
30190
+ style: styles$o.spaceUnder,
30191
+ onChange: newCoords => onChangeProps({
30192
+ radius: newCoords
30193
+ })
30194
+ }), /*#__PURE__*/React__namespace.createElement(AngleInput, {
30195
+ angle: angle,
30196
+ onChange: newAngle => onChangeProps({
30197
+ angle: newAngle
30198
+ })
30199
+ }), /*#__PURE__*/React__namespace.createElement(Strut, {
30200
+ size: wonderBlocksTokens.spacing.xSmall_8
30201
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30202
+ style: [styles$o.row, styles$o.spaceUnder]
30203
+ }, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30204
+ selectedValue: color,
30205
+ onChange: handleColorChange
30206
+ }), /*#__PURE__*/React__namespace.createElement(Strut, {
30207
+ size: wonderBlocksTokens.spacing.medium_16
30208
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30209
+ tag: "label",
30210
+ style: styles$o.row
30211
+ }, "fill", /*#__PURE__*/React__namespace.createElement(Strut, {
30212
+ size: wonderBlocksTokens.spacing.xxSmall_6
30213
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30214
+ selectedValue: fillStyle,
30215
+ onChange: value => onChangeProps({
30216
+ fillStyle: value
30217
+ })
30218
+ // Placeholder is required, but never gets used.
30219
+ ,
30220
+ placeholder: ""
30221
+ }, Object.keys(perseus.lockedFigureFillStyles).map(option => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30222
+ key: option,
30223
+ value: option,
30224
+ label: option
30225
+ }, option))))), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30226
+ tag: "label",
30227
+ style: styles$o.row
30228
+ }, "stroke", /*#__PURE__*/React__namespace.createElement(Strut, {
30229
+ size: wonderBlocksTokens.spacing.xxSmall_6
30230
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30231
+ selectedValue: strokeStyle,
30232
+ onChange: value => onChangeProps({
30233
+ strokeStyle: value
30234
+ })
30235
+ // Placeholder is required, but never gets used.
30236
+ ,
30237
+ placeholder: ""
30238
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30239
+ value: "solid",
30240
+ label: "solid"
30241
+ }, "solid"), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30242
+ value: "dashed",
30243
+ label: "dashed"
30244
+ }, "dashed"))), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30245
+ showM2Features: props.showM2Features,
30246
+ figureType: props.type,
30247
+ onMove: onMove,
30248
+ onRemove: onRemove
30249
+ }));
30254
30250
  };
30255
- ({
30256
- large: {
30257
- query: "(min-width: 1px)",
30258
- totalColumns: 12,
30259
- gutterWidth: wonderBlocksTokens.spacing.xLarge_32,
30260
- marginWidth: wonderBlocksTokens.spacing.medium_16
30261
- }
30262
- });
30263
- ({
30264
- small: {
30265
- query: "(max-width: 767px)",
30266
- totalColumns: 4,
30267
- gutterWidth: wonderBlocksTokens.spacing.medium_16,
30268
- marginWidth: wonderBlocksTokens.spacing.medium_16
30251
+ const styles$o = aphrodite.StyleSheet.create({
30252
+ row: {
30253
+ display: "flex",
30254
+ flexDirection: "row",
30255
+ alignItems: "center"
30269
30256
  },
30270
- large: {
30271
- query: "(min-width: 768px)",
30272
- totalColumns: 12,
30273
- gutterWidth: wonderBlocksTokens.spacing.xLarge_32,
30274
- marginWidth: wonderBlocksTokens.spacing.xxLarge_48
30257
+ spaceUnder: {
30258
+ marginBottom: wonderBlocksTokens.spacing.xSmall_8
30275
30259
  }
30276
30260
  });
30277
30261
 
30278
- const defaultContext = {
30279
- ssrSize: "large",
30280
- mediaSpec: MEDIA_DEFAULT_SPEC
30262
+ const LabeledSwitch = props => {
30263
+ const {
30264
+ checked,
30265
+ label,
30266
+ style,
30267
+ onChange
30268
+ } = props;
30269
+ const ids = wonderBlocksCore.useUniqueIdWithMock();
30270
+ const switchId = ids.get("switch");
30271
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30272
+ style: [styles$n.row, style]
30273
+ }, /*#__PURE__*/React__namespace.createElement(Switch__default["default"], {
30274
+ id: switchId,
30275
+ checked: checked,
30276
+ onChange: onChange
30277
+ }), /*#__PURE__*/React__namespace.createElement(Strut, {
30278
+ size: wonderBlocksTokens.spacing.xSmall_8
30279
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30280
+ tag: "label",
30281
+ htmlFor: switchId
30282
+ }, label));
30281
30283
  };
30282
- const MediaLayoutContext = React__namespace.createContext(defaultContext);
30283
- MediaLayoutContext.displayName = "MediaLayoutContext";
30284
- aphrodite.StyleSheet.create({
30285
- grow: {
30286
- flexGrow: 1
30284
+ const styles$n = aphrodite.StyleSheet.create({
30285
+ row: {
30286
+ flexDirection: "row",
30287
+ alignItems: "center"
30287
30288
  }
30288
30289
  });
30289
30290
 
30290
- class Strut extends React__namespace.Component {
30291
- render() {
30292
- const {
30293
- size,
30294
- style
30295
- } = this.props;
30296
- return React__namespace.createElement(wonderBlocksCore.View, {
30297
- "aria-hidden": "true",
30298
- style: [strutStyle(size), style]
30299
- });
30300
- }
30301
- }
30302
- const strutStyle = size => {
30303
- return {
30304
- width: size,
30305
- MsFlexBasis: size,
30306
- MsFlexPreferredSize: size,
30307
- WebkitFlexBasis: size,
30308
- flexBasis: size,
30309
- flexShrink: 0
30310
- };
30311
- };
30312
-
30313
30291
  /**
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.
30292
+ * DefiningPointSettings is a component that allows the user to edit the
30293
+ * settings of a point that defines a locked figure (ex. line) on the graph.
30317
30294
  *
30318
30295
  * Used in the interactive graph editor's locked figures section.
30319
30296
  */
30320
- const LockedFigureSelect = props => {
30297
+ const DefiningPointSettings = props => {
30321
30298
  const {
30322
- id,
30323
- onChange
30299
+ coord,
30300
+ color: pointColor,
30301
+ filled = true,
30302
+ label,
30303
+ showPoint = "false",
30304
+ error,
30305
+ onChangeProps,
30306
+ onTogglePoint,
30307
+ expanded,
30308
+ onToggle
30324
30309
  } = props;
30325
- const figureTypes = props.showM2Features ? ["point", "line", "ellipse", "vector"] : ["point", "line"];
30326
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30327
- style: styles$r.container
30328
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionMenu, {
30329
- menuText: "Add locked figure",
30330
- style: styles$r.addElementSelect
30331
- }, figureTypes.map(figureType => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.ActionItem, {
30332
- key: `${id}-${figureType}`,
30333
- label: figureType,
30334
- onClick: () => onChange(figureType)
30335
- }, figureType))));
30336
- };
30337
- const styles$r = aphrodite.StyleSheet.create({
30338
- container: {
30339
- marginTop: wonderBlocksTokens.spacing.xSmall_8
30340
- },
30341
- addElementSelect: {
30342
- backgroundColor: wonderBlocksTokens.color.fadedBlue8,
30343
- borderRadius: wonderBlocksTokens.spacing.xxxSmall_4
30310
+ function handleColorChange(newValue) {
30311
+ onChangeProps({
30312
+ color: newValue
30313
+ });
30344
30314
  }
30345
- });
30346
-
30347
- const {
30348
- InfoTip: InfoTip$d
30349
- } = perseus.components;
30350
- const degreeToRadian = degrees => {
30351
- return degrees / 180 * Math.PI;
30352
- };
30353
- const AngleInput = props => {
30354
- const {
30355
- angle,
30356
- onChange
30357
- } = props;
30358
- const [angleInput, setAngleInput] = React__namespace.useState(angle.toString());
30359
- const [isInDegrees, setIsInDegrees] = React__namespace.useState(false);
30360
- function handleAngleChange(newValue) {
30361
- let useDegrees = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : isInDegrees;
30362
- // Update the local state (update the input field value).
30363
- setAngleInput(newValue);
30364
- try {
30365
- // If the new value is a valid expression, update the props.
30366
- // Save the angle in radians.
30367
- const evaluatedAngle = KAS__namespace.parse(newValue).expr.eval();
30368
- if (useDegrees) {
30369
- onChange(degreeToRadian(evaluatedAngle));
30370
- } else {
30371
- onChange(evaluatedAngle);
30372
- }
30373
- } catch (e) {
30374
- // The user likely has not finished typing the expression.
30375
- // Do nothing.
30376
- return;
30315
+ return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30316
+ expanded: expanded,
30317
+ onToggle: onToggle,
30318
+ containerStyle: styles$m.container,
30319
+ panelStyle: styles$m.accordionPanel,
30320
+ header:
30321
+ /*#__PURE__*/
30322
+ // Summary: Point, coords, color (filled/open)
30323
+ React__namespace.createElement(wonderBlocksCore.View, {
30324
+ style: styles$m.row
30325
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `${label || "Point"} (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React__namespace.createElement(Strut, {
30326
+ size: wonderBlocksTokens.spacing.xSmall_8
30327
+ }), showPoint && /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
30328
+ color: pointColor,
30329
+ filled: filled
30330
+ }))
30331
+ }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30332
+ coord: coord,
30333
+ error: !!error,
30334
+ style: styles$m.spaceUnder,
30335
+ onChange: newCoords => {
30336
+ onChangeProps({
30337
+ coord: newCoords
30338
+ });
30377
30339
  }
30378
- }
30379
- function handleAngleTypeChange() {
30380
- // Change the angle based on the new angle type.
30381
- handleAngleChange(angleInput, !isInDegrees);
30382
-
30383
- // Update the angle to the new type.
30384
- setIsInDegrees(usingDegrees => !usingDegrees);
30385
- }
30386
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30387
- style: [styles$q.row, styles$q.spaceUnder]
30388
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30389
- tag: "label",
30390
- style: styles$q.row
30391
- }, "angle", /*#__PURE__*/React__namespace.createElement(Strut, {
30392
- size: wonderBlocksTokens.spacing.xxSmall_6
30393
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
30394
- value: angleInput,
30395
- onChange: handleAngleChange,
30396
- style: styles$q.textField
30397
- })), /*#__PURE__*/React__namespace.createElement(Strut, {
30398
- size: wonderBlocksTokens.spacing.xxSmall_6
30399
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, null, "radians"), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30400
- style: styles$q.switch
30401
- }, /*#__PURE__*/React__namespace.createElement(Switch__default["default"], {
30402
- onChange: handleAngleTypeChange,
30403
- checked: isInDegrees
30404
- })), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, null, "degrees"), /*#__PURE__*/React__namespace.createElement(InfoTip$d, null, /*#__PURE__*/React__namespace.createElement("p", null, "The angle of rotation for the ellipse (if the x radius and y radius are different)."), /*#__PURE__*/React__namespace.createElement("p", null, "Expressions will be evaluted (e.g. \"pi/2\" or \"5pi/4\").")));
30340
+ }), onTogglePoint && /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30341
+ label: "show point on graph",
30342
+ checked: !!showPoint,
30343
+ style: showPoint && styles$m.spaceUnder,
30344
+ onChange: onTogglePoint
30345
+ }), showPoint && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30346
+ selectedValue: pointColor,
30347
+ onChange: handleColorChange,
30348
+ style: styles$m.spaceUnder
30349
+ }), /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30350
+ label: "open point",
30351
+ checked: !filled,
30352
+ onChange: newValue => {
30353
+ onChangeProps({
30354
+ filled: !newValue
30355
+ });
30356
+ }
30357
+ })));
30405
30358
  };
30406
- const styles$q = aphrodite.StyleSheet.create({
30359
+ const styles$m = aphrodite.StyleSheet.create({
30360
+ container: {
30361
+ marginTop: wonderBlocksTokens.spacing.xSmall_8,
30362
+ marginBottom: 0,
30363
+ marginLeft: -wonderBlocksTokens.spacing.xxxSmall_4,
30364
+ marginRight: -wonderBlocksTokens.spacing.xxxSmall_4,
30365
+ backgroundColor: wonderBlocksTokens.color.white
30366
+ },
30367
+ accordionPanel: {
30368
+ // Need more space since we don't have the actions' margins.
30369
+ paddingBottom: wonderBlocksTokens.spacing.medium_16
30370
+ },
30407
30371
  row: {
30408
- display: "flex",
30409
30372
  flexDirection: "row",
30410
30373
  alignItems: "center"
30411
30374
  },
30412
- switch: {
30413
- marginLeft: wonderBlocksTokens.spacing.xxSmall_6,
30414
- marginRight: wonderBlocksTokens.spacing.xxSmall_6
30415
- },
30416
- textField: {
30417
- maxWidth: wonderBlocksTokens.spacing.xxxLarge_64
30375
+ spaceUnder: {
30376
+ marginBottom: wonderBlocksTokens.spacing.xSmall_8
30418
30377
  }
30419
30378
  });
30420
30379
 
30421
- const ColorSwatch = props => {
30380
+ const LineSwatch = props => {
30422
30381
  const {
30423
30382
  color,
30424
- filled = true,
30425
- decorative = false
30383
+ lineStyle
30426
30384
  } = props;
30427
30385
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30428
- "aria-label":
30429
- // Don't include an aria label for decorative circles.
30430
- !decorative ? `${color}, ${filled ? "filled" : "open"}` : undefined,
30431
- style: [styles$p.colorSwatch, {
30432
- border: `4px solid ${perseus.lockedFigureColors[color]}`,
30433
- backgroundColor: filled ? perseus.lockedFigureColors[color] : wonderBlocksTokens.color.white
30386
+ style: styles$l.container
30387
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30388
+ "aria-label": `${color}, ${lineStyle}`,
30389
+ style: [styles$l.lineSwatch, {
30390
+ border: `5px ${lineStyle} ${perseus.lockedFigureColors[color]}`
30434
30391
  }]
30435
- });
30392
+ }));
30436
30393
  };
30437
- const styles$p = aphrodite.StyleSheet.create({
30438
- colorSwatch: {
30439
- // Add a white outline so that the color swatch is visible when
30440
- // the dropdown option is highlighted with its blue background.
30441
- outline: `2px solid ${wonderBlocksTokens.color.offWhite}`,
30442
- borderRadius: "50%",
30443
- width: wonderBlocksTokens.spacing.large_24,
30444
- height: wonderBlocksTokens.spacing.large_24
30394
+ const styles$l = aphrodite.StyleSheet.create({
30395
+ container: {
30396
+ backgroundColor: wonderBlocksTokens.color.white,
30397
+ justifyContent: "center",
30398
+ padding: wonderBlocksTokens.spacing.xSmall_8,
30399
+ borderRadius: wonderBlocksTokens.spacing.xxxSmall_4
30400
+ },
30401
+ lineSwatch: {
30402
+ width: 40
30445
30403
  }
30446
30404
  });
30447
30405
 
30448
- const possibleColors = Object.keys(perseus.lockedFigureColors);
30449
- const ColorSelect = props => {
30406
+ const lengthZeroStr = "The line cannot have length 0.";
30407
+ const LockedLineSettings = props => {
30450
30408
  const {
30451
- selectedValue,
30452
- style,
30453
- onChange
30409
+ kind,
30410
+ points,
30411
+ color: lineColor,
30412
+ lineStyle = "solid",
30413
+ showPoint1,
30414
+ showPoint2,
30415
+ onChangeProps,
30416
+ onMove,
30417
+ onRemove
30454
30418
  } = props;
30455
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30456
- style: [styles$o.row, style]
30419
+ const [point1, point2] = points;
30420
+ const capitalizeKind = kind.charAt(0).toUpperCase() + kind.slice(1);
30421
+ const lineLabel = `${capitalizeKind} (${point1.coord[0]},
30422
+ ${point1.coord[1]}), (${point2.coord[0]}, ${point2.coord[1]})`;
30423
+
30424
+ // Check if the line has length 0.
30425
+ const isInvalid = kmath.vector.equal(point1.coord, point2.coord);
30426
+ function handleChangePoint(newPointProps, index) {
30427
+ const newPoints = [...points];
30428
+ newPoints[index] = {
30429
+ ...points[index],
30430
+ ...newPointProps
30431
+ };
30432
+ onChangeProps({
30433
+ points: newPoints
30434
+ });
30435
+ }
30436
+ function handleColorChange(newColor) {
30437
+ onChangeProps({
30438
+ color: newColor,
30439
+ // Keep the line's points' colors in sync with the line color.
30440
+ points: [{
30441
+ ...point1,
30442
+ color: newColor
30443
+ }, {
30444
+ ...point2,
30445
+ color: newColor
30446
+ }]
30447
+ });
30448
+ }
30449
+ return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30450
+ expanded: props.expanded,
30451
+ onToggle: props.onToggle,
30452
+ header: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30453
+ style: styles$k.row
30454
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, lineLabel), /*#__PURE__*/React__namespace.createElement(Strut, {
30455
+ size: wonderBlocksTokens.spacing.xSmall_8
30456
+ }), /*#__PURE__*/React__namespace.createElement(LineSwatch, {
30457
+ color: lineColor,
30458
+ lineStyle: lineStyle
30459
+ }))
30457
30460
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30458
30461
  tag: "label",
30459
- style: styles$o.row
30460
- }, "color", /*#__PURE__*/React__namespace.createElement(Strut, {
30461
- size: wonderBlocksTokens.spacing.xxSmall_6
30462
+ style: [styles$k.row, styles$k.spaceUnder]
30463
+ }, "kind", /*#__PURE__*/React__namespace.createElement(Strut, {
30464
+ size: wonderBlocksTokens.spacing.xxxSmall_4
30462
30465
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30463
- selectedValue: selectedValue,
30464
- onChange: onChange
30466
+ selectedValue: kind,
30467
+ onChange: value => onChangeProps({
30468
+ kind: value
30469
+ })
30465
30470
  // Placeholder is required, but never gets used.
30466
30471
  ,
30467
30472
  placeholder: ""
30468
- }, possibleColors.map(colorName => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30469
- key: colorName,
30470
- value: colorName,
30471
- label: colorName,
30472
- leftAccessory: /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
30473
- color: colorName,
30474
- decorative: true
30473
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30474
+ value: "line",
30475
+ label: "line"
30476
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30477
+ value: "ray",
30478
+ label: "ray"
30479
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30480
+ value: "segment",
30481
+ label: "segment"
30482
+ }))), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30483
+ style: styles$k.row
30484
+ }, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30485
+ selectedValue: lineColor,
30486
+ onChange: handleColorChange
30487
+ }), /*#__PURE__*/React__namespace.createElement(Strut, {
30488
+ size: wonderBlocksTokens.spacing.small_12
30489
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30490
+ tag: "label",
30491
+ style: styles$k.row
30492
+ }, "style", /*#__PURE__*/React__namespace.createElement(Strut, {
30493
+ size: wonderBlocksTokens.spacing.xxxSmall_4
30494
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30495
+ selectedValue: lineStyle,
30496
+ onChange: value => onChangeProps({
30497
+ lineStyle: value
30475
30498
  })
30476
- }, colorName)))));
30499
+ // Placeholder is required, but never gets used.
30500
+ ,
30501
+ placeholder: "",
30502
+ style: styles$k.selectMarginOffset
30503
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30504
+ value: "solid",
30505
+ label: "solid"
30506
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30507
+ value: "dashed",
30508
+ label: "dashed"
30509
+ })))), isInvalid && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30510
+ style: styles$k.errorText
30511
+ }, lengthZeroStr), /*#__PURE__*/React__namespace.createElement(DefiningPointSettings, _extends({
30512
+ label: "Point 1",
30513
+ showPoint: showPoint1,
30514
+ error: isInvalid ? lengthZeroStr : null
30515
+ }, point1, {
30516
+ onTogglePoint: newValue => onChangeProps({
30517
+ showPoint1: newValue
30518
+ }),
30519
+ onChangeProps: newProps => handleChangePoint(newProps, 0)
30520
+ })), /*#__PURE__*/React__namespace.createElement(DefiningPointSettings, _extends({
30521
+ label: "Point 2",
30522
+ showPoint: showPoint2,
30523
+ error: isInvalid ? lengthZeroStr : null
30524
+ }, point2, {
30525
+ onTogglePoint: newValue => onChangeProps({
30526
+ showPoint2: newValue
30527
+ }),
30528
+ onChangeProps: newProps => handleChangePoint(newProps, 1)
30529
+ })), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30530
+ showM2Features: props.showM2Features,
30531
+ figureType: props.type,
30532
+ onMove: onMove,
30533
+ onRemove: onRemove
30534
+ }));
30477
30535
  };
30478
- const styles$o = aphrodite.StyleSheet.create({
30536
+ const styles$k = aphrodite.StyleSheet.create({
30479
30537
  row: {
30480
30538
  display: "flex",
30481
30539
  flexDirection: "row",
30482
30540
  alignItems: "center"
30541
+ },
30542
+ spaceUnder: {
30543
+ marginBottom: wonderBlocksTokens.spacing.xSmall_8
30544
+ },
30545
+ selectMarginOffset: {
30546
+ // Align with the point settings accordions.
30547
+ marginInlineEnd: -wonderBlocksTokens.spacing.xxxSmall_4
30548
+ },
30549
+ errorText: {
30550
+ color: wonderBlocksTokens.color.red
30483
30551
  }
30484
30552
  });
30485
30553
 
30486
- const CoordinatePairInput = props => {
30554
+ /**
30555
+ * LockedPointSettings is a component that allows the user to edit the
30556
+ * settings of specifically a locked point on the graph.
30557
+ *
30558
+ * Used in the interactive graph editor's locked figures section.
30559
+ */
30560
+ const LockedPointSettings = props => {
30487
30561
  const {
30488
30562
  coord,
30489
- labels,
30490
- error,
30491
- onChange
30563
+ color: pointColor,
30564
+ filled = true,
30565
+ onChangeProps,
30566
+ onMove,
30567
+ onRemove
30492
30568
  } = props;
30493
-
30494
- // Keep track of the coordinates via state as the user is editing them,
30495
- // before they are updated in the props as a valid number.
30496
- const [coordState, setCoordState] = React__namespace.useState([
30497
- // Using strings to make it easier to work with the text fields.
30498
- coord[0].toString(), coord[1].toString()]);
30499
- function handleCoordChange(newValue, coordIndex) {
30500
- // Update the local state (update the input field value).
30501
- const newCoordState = [...coordState];
30502
- newCoordState[coordIndex] = newValue;
30503
- setCoordState(newCoordState);
30504
-
30505
- // If the new value is not a number, don't update the props.
30506
- // If it's empty, keep the props the same value instead of setting to 0.
30507
- if (isNaN(+newValue) || newValue === "") {
30508
- return;
30509
- }
30510
-
30511
- // Update the props (update the graph).
30512
- const newCoords = [...coord];
30513
- newCoords[coordIndex] = +newValue;
30514
- onChange(newCoords);
30569
+ function handleColorChange(newValue) {
30570
+ onChangeProps({
30571
+ color: newValue
30572
+ });
30515
30573
  }
30516
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30517
- style: [styles$n.row, styles$n.spaceUnder]
30518
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30519
- tag: "label",
30520
- style: styles$n.row
30521
- }, labels ? labels[0] : "x coord", /*#__PURE__*/React__namespace.createElement(Strut, {
30522
- size: wonderBlocksTokens.spacing.xxSmall_6
30523
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
30524
- type: "number",
30525
- value: coordState[0],
30526
- onChange: newValue => handleCoordChange(newValue, 0),
30527
- style: [styles$n.textField, error ? styles$n.errorField : undefined]
30528
- })), /*#__PURE__*/React__namespace.createElement(Strut, {
30529
- size: wonderBlocksTokens.spacing.medium_16
30530
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30531
- tag: "label",
30532
- style: styles$n.row
30533
- }, labels ? labels[1] : "y coord", /*#__PURE__*/React__namespace.createElement(Strut, {
30534
- size: wonderBlocksTokens.spacing.xxSmall_6
30535
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
30536
- type: "number",
30537
- value: coordState[1],
30538
- onChange: newValue => handleCoordChange(newValue, 1),
30539
- style: [styles$n.textField, error ? styles$n.errorField : undefined]
30540
- }))));
30574
+ return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30575
+ expanded: props.expanded,
30576
+ onToggle: props.onToggle,
30577
+ header:
30578
+ /*#__PURE__*/
30579
+ // Summary: Point, coords, color (filled/open)
30580
+ React__namespace.createElement(wonderBlocksCore.View, {
30581
+ style: styles$j.row
30582
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Point (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React__namespace.createElement(Strut, {
30583
+ size: wonderBlocksTokens.spacing.xSmall_8
30584
+ }), /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
30585
+ color: pointColor,
30586
+ filled: filled
30587
+ }))
30588
+ }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30589
+ coord: coord,
30590
+ style: styles$j.spaceUnder,
30591
+ onChange: newCoord => {
30592
+ onChangeProps({
30593
+ coord: newCoord
30594
+ });
30595
+ }
30596
+ }), /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30597
+ selectedValue: pointColor,
30598
+ onChange: handleColorChange,
30599
+ style: styles$j.spaceUnder
30600
+ }), /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30601
+ label: "open point",
30602
+ checked: !filled,
30603
+ onChange: newValue => {
30604
+ onChangeProps({
30605
+ filled: !newValue
30606
+ });
30607
+ }
30608
+ }), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30609
+ showM2Features: props.showM2Features,
30610
+ figureType: props.type,
30611
+ onMove: onMove,
30612
+ onRemove: onRemove
30613
+ }));
30541
30614
  };
30542
- const styles$n = aphrodite.StyleSheet.create({
30615
+ const styles$j = aphrodite.StyleSheet.create({
30543
30616
  row: {
30544
- display: "flex",
30545
30617
  flexDirection: "row",
30546
30618
  alignItems: "center"
30547
30619
  },
30548
30620
  spaceUnder: {
30549
30621
  marginBottom: wonderBlocksTokens.spacing.xSmall_8
30550
- },
30551
- textField: {
30552
- width: wonderBlocksTokens.spacing.xxxLarge_64
30553
- },
30554
- errorField: {
30555
- borderColor: wonderBlocksTokens.color.red,
30556
- backgroundColor: wonderBlocksTokens.color.fadedRed8
30557
30622
  }
30558
30623
  });
30559
30624
 
30560
- const EllipseSwatch = props => {
30625
+ const PolygonSwatch = props => {
30561
30626
  const {
30562
30627
  color,
30563
30628
  fillStyle,
@@ -30565,128 +30630,44 @@ const EllipseSwatch = props => {
30565
30630
  } = props;
30566
30631
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30567
30632
  "aria-label": `${color}, stroke ${strokeStyle}, fill ${fillStyle}`,
30568
- style: [styles$m.container, {
30633
+ style: [styles$i.container, {
30569
30634
  border: `4px ${strokeStyle} ${perseus.lockedFigureColors[color]}`
30570
30635
  }]
30571
30636
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30572
- style: [styles$m.innerCircle, {
30637
+ style: [styles$i.innerSquare, {
30573
30638
  backgroundColor: perseus.lockedFigureColors[color],
30574
- opacity: perseus.lockedEllipseFillStyles[fillStyle]
30639
+ opacity: perseus.lockedFigureFillStyles[fillStyle]
30575
30640
  }]
30576
30641
  }));
30577
30642
  };
30578
- const styles$m = aphrodite.StyleSheet.create({
30643
+ const styles$i = aphrodite.StyleSheet.create({
30579
30644
  container: {
30580
30645
  // Add a white outline so that the color swatch is visible when
30581
30646
  // the dropdown option is highlighted with its blue background.
30582
30647
  outline: `2px solid ${wonderBlocksTokens.color.offWhite}`,
30583
- borderRadius: "50%",
30584
- width: wonderBlocksTokens.spacing.xLarge_32,
30648
+ width: wonderBlocksTokens.spacing.large_24,
30585
30649
  height: wonderBlocksTokens.spacing.large_24,
30586
30650
  backgroundColor: wonderBlocksTokens.color.white,
30587
30651
  alignItems: "center",
30588
30652
  justifyContent: "center"
30589
30653
  },
30590
- innerCircle: {
30591
- width: 28,
30592
- height: 20,
30593
- borderRadius: "50%"
30594
- }
30595
- });
30596
-
30597
- const LockedFigureSettingsAccordion = props => {
30598
- const {
30599
- children,
30600
- header,
30601
- expanded,
30602
- containerStyle,
30603
- panelStyle,
30604
- onToggle
30605
- } = props;
30606
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View
30607
- // More specificity so that we can override the default
30608
- // .heading > h2 > .header styles from the articles.less
30609
- // file (which is imported in perseus-renderer.less).
30610
- , {
30611
- className: "locked-figure-accordion"
30612
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksAccordion.AccordionSection, {
30613
- expanded: expanded,
30614
- onToggle: onToggle,
30615
- style: [styles$l.container, containerStyle],
30616
- headerStyle: styles$l.accordionHeader,
30617
- header: header
30618
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30619
- style: [styles$l.accordionPanel, panelStyle]
30620
- }, children)));
30621
- };
30622
- const styles$l = aphrodite.StyleSheet.create({
30623
- container: {
30624
- backgroundColor: wonderBlocksTokens.color.fadedBlue8,
30625
- marginTop: wonderBlocksTokens.spacing.xSmall_8
30626
- },
30627
- accordionHeader: {
30628
- padding: wonderBlocksTokens.spacing.small_12,
30629
- // Don't move the dropdown caret.
30630
- paddingInlineEnd: 0,
30631
- // Fixed height so the addition of the color swatch doesn't
30632
- // change the height of the header when toggling.
30633
- height: wonderBlocksTokens.spacing.xxLarge_48
30634
- },
30635
- accordionPanel: {
30636
- paddingTop: wonderBlocksTokens.spacing.xxSmall_6,
30637
- paddingBottom: wonderBlocksTokens.spacing.xxxSmall_4,
30638
- paddingLeft: wonderBlocksTokens.spacing.small_12,
30639
- paddingRight: wonderBlocksTokens.spacing.small_12
30640
- }
30641
- });
30642
-
30643
- /**
30644
- * The part of a locked figure settings panel that contains actions
30645
- * for this panel and its associated locked figure, including
30646
- * the delete button.
30647
- */
30648
- const LockedFigureSettingsActions = props => {
30649
- const {
30650
- onRemove,
30651
- figureAriaLabel
30652
- } = props;
30653
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30654
- style: styles$k.container
30655
- }, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
30656
- startIcon: trashIcon__default["default"],
30657
- "aria-label": `Delete ${figureAriaLabel}`,
30658
- onClick: onRemove,
30659
- kind: "tertiary",
30660
- style: styles$k.deleteButton
30661
- }, "Delete"));
30662
- };
30663
- const styles$k = aphrodite.StyleSheet.create({
30664
- container: {
30665
- width: "100%",
30666
- flexDirection: "row",
30667
- alignItems: "center",
30668
- marginTop: wonderBlocksTokens.spacing.xxxSmall_4
30669
- },
30670
- deleteButton: {
30671
- // Line up the delete icon with the rest of the content.
30672
- marginInlineStart: -wonderBlocksTokens.spacing.xxxSmall_4
30654
+ innerSquare: {
30655
+ width: 20,
30656
+ height: 20
30673
30657
  }
30674
30658
  });
30675
30659
 
30676
- const {
30677
- InfoTip: InfoTip$c
30678
- } = perseus.components;
30679
- const LockedEllipseSettings = props => {
30660
+ const LockedPolygonSettings = props => {
30680
30661
  const {
30681
- center,
30682
- radius,
30683
- angle,
30662
+ points,
30684
30663
  color,
30664
+ showVertices,
30685
30665
  fillStyle,
30686
30666
  strokeStyle,
30687
30667
  expanded,
30688
30668
  onToggle,
30689
30669
  onChangeProps,
30670
+ onMove,
30690
30671
  onRemove
30691
30672
  } = props;
30692
30673
  function handleColorChange(newValue) {
@@ -30699,40 +30680,18 @@ const LockedEllipseSettings = props => {
30699
30680
  onToggle: onToggle,
30700
30681
  header:
30701
30682
  /*#__PURE__*/
30702
- // Summary: Ellipse, center, radius, color (opacity, dashed)
30683
+ // Summary: Polygon, number of sides, style swatch
30703
30684
  React__namespace.createElement(wonderBlocksCore.View, {
30704
- style: styles$j.row
30705
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Ellipse (${center[0]}, ${center[1]}), radius ${radius[0]}, ${radius[1]}`), /*#__PURE__*/React__namespace.createElement(Strut, {
30685
+ style: styles$h.row
30686
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Polygon, ${points.length} sides`), /*#__PURE__*/React__namespace.createElement(Strut, {
30706
30687
  size: wonderBlocksTokens.spacing.xSmall_8
30707
- }), /*#__PURE__*/React__namespace.createElement(EllipseSwatch, {
30708
- color: props.color,
30688
+ }), /*#__PURE__*/React__namespace.createElement(PolygonSwatch, {
30689
+ color: color,
30709
30690
  fillStyle: fillStyle,
30710
30691
  strokeStyle: strokeStyle
30711
30692
  }))
30712
30693
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30713
- style: styles$j.row
30714
- }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30715
- coord: center,
30716
- onChange: newCoords => onChangeProps({
30717
- center: newCoords
30718
- })
30719
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30720
- style: styles$j.spaceUnder
30721
- }, /*#__PURE__*/React__namespace.createElement(InfoTip$c, null, "The coordinates for the center of the ellipse."))), /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30722
- coord: radius,
30723
- labels: ["x radius", "y radius"],
30724
- onChange: newCoords => onChangeProps({
30725
- radius: newCoords
30726
- })
30727
- }), /*#__PURE__*/React__namespace.createElement(AngleInput, {
30728
- angle: angle,
30729
- onChange: newAngle => onChangeProps({
30730
- angle: newAngle
30731
- })
30732
- }), /*#__PURE__*/React__namespace.createElement(Strut, {
30733
- size: wonderBlocksTokens.spacing.xSmall_8
30734
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30735
- style: [styles$j.row, styles$j.spaceUnder]
30694
+ style: [styles$h.row, styles$h.spaceUnder]
30736
30695
  }, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30737
30696
  selectedValue: color,
30738
30697
  onChange: handleColorChange
@@ -30740,7 +30699,7 @@ const LockedEllipseSettings = props => {
30740
30699
  size: wonderBlocksTokens.spacing.medium_16
30741
30700
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30742
30701
  tag: "label",
30743
- style: styles$j.row
30702
+ style: styles$h.row
30744
30703
  }, "fill", /*#__PURE__*/React__namespace.createElement(Strut, {
30745
30704
  size: wonderBlocksTokens.spacing.xxSmall_6
30746
30705
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
@@ -30751,13 +30710,13 @@ const LockedEllipseSettings = props => {
30751
30710
  // Placeholder is required, but never gets used.
30752
30711
  ,
30753
30712
  placeholder: ""
30754
- }, Object.keys(perseus.lockedEllipseFillStyles).map(option => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30713
+ }, Object.keys(perseus.lockedFigureFillStyles).map(option => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30755
30714
  key: option,
30756
30715
  value: option,
30757
30716
  label: option
30758
30717
  }, option))))), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30759
30718
  tag: "label",
30760
- style: styles$j.row
30719
+ style: [styles$h.row, styles$h.spaceUnder]
30761
30720
  }, "stroke", /*#__PURE__*/React__namespace.createElement(Strut, {
30762
30721
  size: wonderBlocksTokens.spacing.xxSmall_6
30763
30722
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
@@ -30774,683 +30733,1020 @@ const LockedEllipseSettings = props => {
30774
30733
  }, "solid"), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
30775
30734
  value: "dashed",
30776
30735
  label: "dashed"
30777
- }, "dashed"))), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30778
- onRemove: onRemove,
30779
- figureAriaLabel: `locked ellipse at ${center[0]}, ${center[1]}`
30780
- }));
30781
- };
30782
- const styles$j = aphrodite.StyleSheet.create({
30783
- row: {
30784
- display: "flex",
30785
- flexDirection: "row",
30786
- alignItems: "center"
30787
- },
30788
- spaceUnder: {
30789
- marginBottom: wonderBlocksTokens.spacing.xSmall_8
30790
- }
30791
- });
30792
-
30793
- const LabeledSwitch = props => {
30794
- const {
30795
- checked,
30796
- label,
30797
- style,
30798
- onChange
30799
- } = props;
30800
- const ids = wonderBlocksCore.useUniqueIdWithMock();
30801
- const switchId = ids.get("switch");
30802
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30803
- style: [styles$i.row, style]
30804
- }, /*#__PURE__*/React__namespace.createElement(Switch__default["default"], {
30805
- id: switchId,
30806
- checked: checked,
30807
- onChange: onChange
30808
- }), /*#__PURE__*/React__namespace.createElement(Strut, {
30809
- size: wonderBlocksTokens.spacing.xSmall_8
30810
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30811
- tag: "label",
30812
- htmlFor: switchId
30813
- }, label));
30814
- };
30815
- const styles$i = aphrodite.StyleSheet.create({
30816
- row: {
30817
- flexDirection: "row",
30818
- alignItems: "center"
30819
- }
30820
- });
30821
-
30822
- /**
30823
- * DefiningPointSettings is a component that allows the user to edit the
30824
- * settings of a point that defines a locked figure (ex. line) on the graph.
30825
- *
30826
- * Used in the interactive graph editor's locked figures section.
30827
- */
30828
- const DefiningPointSettings = props => {
30829
- const {
30830
- coord,
30831
- color: pointColor,
30832
- filled = true,
30833
- label,
30834
- showPoint = "false",
30835
- error,
30836
- onChangeProps,
30837
- onTogglePoint
30838
- } = props;
30839
- function handleColorChange(newValue) {
30840
- onChangeProps({
30841
- color: newValue
30842
- });
30843
- }
30844
- return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30845
- expanded: props.expanded,
30846
- onToggle: props.onToggle,
30847
- containerStyle: styles$h.container,
30848
- panelStyle: styles$h.accordionPanel,
30849
- header:
30850
- /*#__PURE__*/
30851
- // Summary: Point, coords, color (filled/open)
30852
- React__namespace.createElement(wonderBlocksCore.View, {
30853
- style: styles$h.row
30854
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `${label || "Point"} (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React__namespace.createElement(Strut, {
30855
- size: wonderBlocksTokens.spacing.xSmall_8
30856
- }), showPoint && /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
30857
- color: pointColor,
30858
- filled: filled
30859
- }))
30860
- }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30861
- coord: coord,
30862
- error: !!error,
30863
- onChange: newCoords => {
30864
- onChangeProps({
30865
- coord: newCoords
30866
- });
30867
- }
30868
- }), onTogglePoint && /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30869
- label: "show point on graph",
30870
- checked: !!showPoint,
30871
- style: showPoint && styles$h.spaceUnder,
30872
- onChange: onTogglePoint
30873
- }), showPoint && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
30874
- selectedValue: pointColor,
30875
- onChange: handleColorChange,
30736
+ }, "dashed"))), /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30737
+ label: "show vertices",
30738
+ checked: showVertices,
30739
+ onChange: newValue => onChangeProps({
30740
+ showVertices: newValue
30741
+ }),
30876
30742
  style: styles$h.spaceUnder
30877
- }), /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
30878
- label: "open point",
30879
- checked: !filled,
30880
- onChange: newValue => {
30881
- onChangeProps({
30882
- filled: !newValue
30883
- });
30884
- }
30885
- })));
30886
- };
30887
- const styles$h = aphrodite.StyleSheet.create({
30888
- container: {
30889
- marginTop: wonderBlocksTokens.spacing.xSmall_8,
30890
- marginBottom: 0,
30891
- marginLeft: -wonderBlocksTokens.spacing.xxxSmall_4,
30892
- marginRight: -wonderBlocksTokens.spacing.xxxSmall_4,
30893
- backgroundColor: wonderBlocksTokens.color.white
30894
- },
30895
- accordionPanel: {
30896
- // Need more space since we don't have the actions' margins.
30897
- paddingBottom: wonderBlocksTokens.spacing.medium_16
30898
- },
30899
- row: {
30900
- flexDirection: "row",
30901
- alignItems: "center"
30902
- },
30903
- spaceUnder: {
30904
- marginBottom: wonderBlocksTokens.spacing.xSmall_8
30905
- }
30906
- });
30907
-
30908
- const LineSwatch = props => {
30909
- const {
30910
- color,
30911
- lineStyle
30912
- } = props;
30913
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30914
- style: styles$g.container
30915
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30916
- "aria-label": `${color}, ${lineStyle}`,
30917
- style: [styles$g.lineSwatch, {
30918
- border: `5px ${lineStyle} ${perseus.lockedFigureColors[color]}`
30919
- }]
30743
+ }), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30744
+ header: /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, "Points"),
30745
+ expanded: true,
30746
+ containerStyle: styles$h.pointAccordionContainer,
30747
+ panelStyle: styles$h.pointAccordionPanel
30748
+ }, points.map((point, index) => {
30749
+ const pointLabel = String.fromCharCode(65 + index);
30750
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30751
+ key: `locked-polygon-point-${point[0]}-${point[1]}-index-${index}`,
30752
+ style: [styles$h.row, styles$h.spaceUnder]
30753
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `${pointLabel}:`), /*#__PURE__*/React__namespace.createElement(Strut, {
30754
+ size: wonderBlocksTokens.spacing.medium_16
30755
+ }), /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30756
+ coord: point,
30757
+ labels: ["x", "y"],
30758
+ onChange: newValue => {
30759
+ const newPoints = [...points];
30760
+ newPoints[index] = newValue;
30761
+ props.onChangeProps({
30762
+ points: newPoints
30763
+ });
30764
+ }
30765
+ }),
30766
+ // Only show the minus (delete) buttons if there are
30767
+ // more than 3 points. 3 points is the minimum number
30768
+ // of points for a polygon (triangle).
30769
+ points.length > 3 && /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
30770
+ "aria-label": `Delete polygon point ${pointLabel}`,
30771
+ icon: minusCircle__default["default"],
30772
+ color: "destructive",
30773
+ onClick: () => {
30774
+ const newPoints = [...points];
30775
+ newPoints.splice(index, 1);
30776
+ props.onChangeProps({
30777
+ points: newPoints
30778
+ });
30779
+ },
30780
+ style: styles$h.icon
30781
+ }));
30782
+ }), /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
30783
+ kind: "tertiary",
30784
+ startIcon: plusCircle__default["default"],
30785
+ onClick: () => {
30786
+ props.onChangeProps({
30787
+ points: [...points, [0, 0]]
30788
+ });
30789
+ }
30790
+ }, "Add point")), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30791
+ showM2Features: props.showM2Features,
30792
+ figureType: props.type,
30793
+ onMove: onMove,
30794
+ onRemove: onRemove
30920
30795
  }));
30921
30796
  };
30922
- const styles$g = aphrodite.StyleSheet.create({
30923
- container: {
30924
- backgroundColor: wonderBlocksTokens.color.white,
30925
- justifyContent: "center",
30926
- padding: wonderBlocksTokens.spacing.xSmall_8,
30927
- borderRadius: wonderBlocksTokens.spacing.xxxSmall_4
30797
+ const styles$h = aphrodite.StyleSheet.create({
30798
+ row: {
30799
+ display: "flex",
30800
+ flexDirection: "row",
30801
+ alignItems: "center"
30928
30802
  },
30929
- lineSwatch: {
30930
- width: 40
30803
+ pointAccordionContainer: {
30804
+ backgroundColor: wonderBlocksTokens.color.white
30805
+ },
30806
+ pointAccordionPanel: {
30807
+ alignItems: "start"
30808
+ },
30809
+ icon: {
30810
+ marginInlineStart: wonderBlocksTokens.spacing.xxxSmall_4
30811
+ },
30812
+ spaceUnder: {
30813
+ marginBottom: wonderBlocksTokens.spacing.xSmall_8
30931
30814
  }
30932
30815
  });
30933
30816
 
30934
- const lengthZeroStr = "The line cannot have length 0.";
30935
- const LockedLineSettings = props => {
30817
+ /**
30818
+ * LockedVectorSettings is a component that allows the user to edit the
30819
+ * settings of specifically a locked vector on the graph.
30820
+ *
30821
+ * Used in the interactive graph editor's locked figures section.
30822
+ */
30823
+ const lengthErrorMessage = "The vector cannot have length 0.";
30824
+ const LockedVectorSettings = props => {
30936
30825
  const {
30937
- kind,
30938
30826
  points,
30939
30827
  color: lineColor,
30940
- lineStyle = "solid",
30941
- showPoint1,
30942
- showPoint2,
30943
30828
  onChangeProps,
30829
+ onMove,
30944
30830
  onRemove
30945
30831
  } = props;
30946
- const [point1, point2] = points;
30947
- const capitalizeKind = kind.charAt(0).toUpperCase() + kind.slice(1);
30948
- const lineLabel = `${capitalizeKind} (${point1.coord[0]},
30949
- ${point1.coord[1]}), (${point2.coord[0]}, ${point2.coord[1]})`;
30832
+ const [tail, tip] = points;
30833
+ const lineLabel = `Vector (${tail[0]}, ${tail[1]}), (${tip[0]}, ${tip[1]})`;
30950
30834
 
30951
30835
  // Check if the line has length 0.
30952
- const isInvalid = kmath.vector.equal(point1.coord, point2.coord);
30953
- function handleChangePoint(newPointProps, index) {
30954
- const newPoints = [...points];
30955
- newPoints[index] = {
30956
- ...points[index],
30957
- ...newPointProps
30958
- };
30959
- onChangeProps({
30960
- points: newPoints
30961
- });
30836
+ const isInvalid = kmath.vector.equal(tail, tip);
30837
+ function handleChangePoint(newCoord, index) {
30838
+ if (typeof newCoord !== "undefined") {
30839
+ const newPoints = [...points];
30840
+ newPoints[index] = [...newCoord];
30841
+ onChangeProps({
30842
+ points: newPoints
30843
+ });
30844
+ }
30962
30845
  }
30963
30846
  function handleColorChange(newColor) {
30964
30847
  onChangeProps({
30965
- color: newColor,
30966
- // Keep the line's points' colors in sync with the line color.
30967
- points: [{
30968
- ...point1,
30969
- color: newColor
30970
- }, {
30971
- ...point2,
30972
- color: newColor
30973
- }]
30848
+ color: newColor
30974
30849
  });
30975
30850
  }
30976
30851
  return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30977
30852
  expanded: props.expanded,
30978
30853
  onToggle: props.onToggle,
30979
30854
  header: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30980
- style: styles$f.row
30855
+ style: styles$g.row
30981
30856
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, lineLabel), /*#__PURE__*/React__namespace.createElement(Strut, {
30982
30857
  size: wonderBlocksTokens.spacing.xSmall_8
30983
30858
  }), /*#__PURE__*/React__namespace.createElement(LineSwatch, {
30984
30859
  color: lineColor,
30985
- lineStyle: lineStyle
30860
+ lineStyle: "solid"
30986
30861
  }))
30987
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30988
- tag: "label",
30989
- style: [styles$f.row, styles$f.spaceUnder]
30990
- }, "kind", /*#__PURE__*/React__namespace.createElement(Strut, {
30991
- size: wonderBlocksTokens.spacing.xxxSmall_4
30992
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
30993
- selectedValue: kind,
30994
- onChange: value => onChangeProps({
30995
- kind: value
30996
- })
30997
- // Placeholder is required, but never gets used.
30998
- ,
30999
- placeholder: ""
31000
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31001
- value: "line",
31002
- label: "line"
31003
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31004
- value: "ray",
31005
- label: "ray"
31006
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31007
- value: "segment",
31008
- label: "segment"
31009
- }))), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31010
- style: styles$f.row
30862
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30863
+ style: [styles$g.row, styles$g.spaceUnder]
31011
30864
  }, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
31012
30865
  selectedValue: lineColor,
31013
30866
  onChange: handleColorChange
30867
+ })), isInvalid && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
30868
+ style: styles$g.errorText
30869
+ }, lengthErrorMessage), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30870
+ expanded: true // Initial state is expanded
30871
+ ,
30872
+ containerStyle: styles$g.container,
30873
+ panelStyle: styles$g.accordionPanel,
30874
+ header: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30875
+ style: styles$g.row
30876
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Tail (${tail[0]}, ${tail[1]})`))
30877
+ }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30878
+ coord: tail,
30879
+ error: isInvalid,
30880
+ onChange: newProps => {
30881
+ handleChangePoint(newProps, 0);
30882
+ }
30883
+ })), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
30884
+ expanded: true // Initial state is expanded
30885
+ ,
30886
+ containerStyle: styles$g.container,
30887
+ panelStyle: styles$g.accordionPanel,
30888
+ header: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30889
+ style: styles$g.row
30890
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Tip (${tip[0]}, ${tip[1]})`))
30891
+ }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
30892
+ coord: tip,
30893
+ error: isInvalid,
30894
+ onChange: newProps => {
30895
+ handleChangePoint(newProps, 1);
30896
+ }
30897
+ })), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
30898
+ showM2Features: props.showM2Features,
30899
+ figureType: props.type,
30900
+ onMove: onMove,
30901
+ onRemove: onRemove
30902
+ }));
30903
+ };
30904
+ const styles$g = aphrodite.StyleSheet.create({
30905
+ accordionPanel: {
30906
+ paddingBottom: wonderBlocksTokens.spacing.medium_16
30907
+ },
30908
+ container: {
30909
+ marginTop: wonderBlocksTokens.spacing.xSmall_8,
30910
+ marginBottom: 0,
30911
+ marginLeft: -wonderBlocksTokens.spacing.xxxSmall_4,
30912
+ marginRight: -wonderBlocksTokens.spacing.xxxSmall_4,
30913
+ backgroundColor: wonderBlocksTokens.color.white
30914
+ },
30915
+ errorText: {
30916
+ color: wonderBlocksTokens.color.red,
30917
+ marginTop: wonderBlocksTokens.spacing.xSmall_8
30918
+ },
30919
+ row: {
30920
+ flexDirection: "row",
30921
+ alignItems: "center"
30922
+ }
30923
+ });
30924
+
30925
+ /**
30926
+ * LockedFigureSettings takes in a figure and renders the
30927
+ * respective settings for that figure type.
30928
+ *
30929
+ * Used in the interactive graph editor's locked figures section.
30930
+ */
30931
+
30932
+ // Union this type with other locked figure types when they are added.
30933
+
30934
+ const LockedFigureSettings = props => {
30935
+ switch (props.type) {
30936
+ case "point":
30937
+ return /*#__PURE__*/React__namespace.createElement(LockedPointSettings, props);
30938
+ case "line":
30939
+ return /*#__PURE__*/React__namespace.createElement(LockedLineSettings, props);
30940
+ case "vector":
30941
+ if (props.showM2Features) {
30942
+ return /*#__PURE__*/React__namespace.createElement(LockedVectorSettings, props);
30943
+ }
30944
+ break;
30945
+ case "ellipse":
30946
+ if (props.showM2Features) {
30947
+ return /*#__PURE__*/React__namespace.createElement(LockedEllipseSettings, props);
30948
+ }
30949
+ break;
30950
+ case "polygon":
30951
+ if (props.showM2Features) {
30952
+ return /*#__PURE__*/React__namespace.createElement(LockedPolygonSettings, props);
30953
+ }
30954
+ }
30955
+ return null;
30956
+ };
30957
+
30958
+ const LockedFiguresSection = props => {
30959
+ // Keep track of all figures' accordions' expanded states for the
30960
+ // expand/collapse all button. Set the whole array to false initially.
30961
+ const collapsedStateArray = Array((props.figures ?? []).length).fill(false);
30962
+ const [expandedStates, setExpandedStates] = React__namespace.useState(collapsedStateArray);
30963
+ const uniqueId = wonderBlocksCore.useUniqueIdWithMock().get("locked-figures-section");
30964
+ const {
30965
+ figures,
30966
+ onChange
30967
+ } = props;
30968
+ function addLockedFigure(newFigure) {
30969
+ const lockedFigures = figures || [];
30970
+ const newProps = {
30971
+ lockedFigures: [...lockedFigures, getDefaultFigureForType(newFigure)]
30972
+ };
30973
+ onChange(newProps);
30974
+ setExpandedStates([...expandedStates, true]);
30975
+ }
30976
+ function moveLockedFigure(index, movement) {
30977
+ // Don't allow moving the first figure up or the last figure down.
30978
+ if (index === 0 && (movement === "back" || movement === "backward")) {
30979
+ return;
30980
+ }
30981
+ if (figures && index === figures.length - 1 && (movement === "front" || movement === "forward")) {
30982
+ return;
30983
+ }
30984
+ const lockedFigures = figures || [];
30985
+ const newFigures = [...lockedFigures];
30986
+ const newExpandedStates = [...expandedStates];
30987
+
30988
+ // First, remove the figure from its current position
30989
+ // in the figures array and the expanded states array.
30990
+ const [removedFigure] = newFigures.splice(index, 1);
30991
+ newExpandedStates.splice(index, 1);
30992
+
30993
+ // Then, add it back in the new position. Add "true" to the
30994
+ // expanded states array for the new position (it must already
30995
+ // be open since the button to move it is being pressed from there).
30996
+ switch (movement) {
30997
+ case "back":
30998
+ newFigures.unshift(removedFigure);
30999
+ newExpandedStates.unshift(true);
31000
+ break;
31001
+ case "backward":
31002
+ newFigures.splice(index - 1, 0, removedFigure);
31003
+ newExpandedStates.splice(index - 1, 0, true);
31004
+ break;
31005
+ case "forward":
31006
+ newFigures.splice(index + 1, 0, removedFigure);
31007
+ newExpandedStates.splice(index + 1, 0, true);
31008
+ break;
31009
+ case "front":
31010
+ newFigures.push(removedFigure);
31011
+ newExpandedStates.push(true);
31012
+ break;
31013
+ }
31014
+ onChange({
31015
+ lockedFigures: newFigures
31016
+ });
31017
+ setExpandedStates(newExpandedStates);
31018
+ }
31019
+ function removeLockedFigure(index) {
31020
+ // eslint-disable-next-line no-alert
31021
+ if (window.confirm("Are you sure you want to delete this figure?")) {
31022
+ const lockedFigures = figures || [];
31023
+ onChange({
31024
+ lockedFigures: [...lockedFigures.slice(0, index), ...lockedFigures.slice(index + 1)]
31025
+ });
31026
+
31027
+ // Update expanded states
31028
+ const newExpandedStates = [...expandedStates];
31029
+ newExpandedStates.splice(index, 1);
31030
+ setExpandedStates(newExpandedStates);
31031
+ }
31032
+ }
31033
+ function changeProps(index,
31034
+ // Omit the type from the figure props so it doesn't think
31035
+ // we're trying to pass in the props for the wrong type.
31036
+ figureProps) {
31037
+ const lockedFigures = figures || [];
31038
+ const newFigures = {
31039
+ lockedFigures: [...lockedFigures.slice(0, index), {
31040
+ ...lockedFigures[index],
31041
+ ...figureProps
31042
+ }, ...lockedFigures.slice(index + 1)]
31043
+ };
31044
+ onChange(newFigures);
31045
+ }
31046
+ function toggleExpanded(newValue) {
31047
+ setExpandedStates(Array(figures?.length).fill(newValue));
31048
+ }
31049
+ const allCollapsed = expandedStates.every(value => !value);
31050
+ const buttonLabel = allCollapsed ? "Expand all" : "Collapse all";
31051
+ const showExpandButton = !!figures?.length;
31052
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, figures?.map((figure, index) => {
31053
+ return /*#__PURE__*/React__namespace.createElement(LockedFigureSettings, _extends({
31054
+ key: `${uniqueId}-locked-${figure}-${index}`,
31055
+ showM2Features: props.showM2Features,
31056
+ showM2bFeatures: props.showM2bFeatures,
31057
+ expanded: expandedStates[index],
31058
+ onToggle: newValue => {
31059
+ const newExpanded = [...expandedStates];
31060
+ newExpanded[index] = newValue;
31061
+ setExpandedStates(newExpanded);
31062
+ }
31063
+ }, figure, {
31064
+ onChangeProps: newProps => changeProps(index, newProps),
31065
+ onMove: movement => moveLockedFigure(index, movement),
31066
+ onRemove: () => removeLockedFigure(index)
31067
+ }));
31068
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31069
+ style: styles$f.buttonContainer
31070
+ }, /*#__PURE__*/React__namespace.createElement(LockedFigureSelect, {
31071
+ showM2Features: props.showM2Features,
31072
+ showM2bFeatures: props.showM2bFeatures,
31073
+ id: `${uniqueId}-select`,
31074
+ onChange: addLockedFigure
31014
31075
  }), /*#__PURE__*/React__namespace.createElement(Strut, {
31015
31076
  size: wonderBlocksTokens.spacing.small_12
31016
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
31017
- tag: "label",
31018
- style: styles$f.row
31019
- }, "style", /*#__PURE__*/React__namespace.createElement(Strut, {
31020
- size: wonderBlocksTokens.spacing.xxxSmall_4
31021
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
31022
- selectedValue: lineStyle,
31023
- onChange: value => onChangeProps({
31024
- lineStyle: value
31025
- })
31026
- // Placeholder is required, but never gets used.
31027
- ,
31028
- placeholder: "",
31029
- style: styles$f.selectMarginOffset
31030
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31031
- value: "solid",
31032
- label: "solid"
31033
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31034
- value: "dashed",
31035
- label: "dashed"
31036
- })))), isInvalid && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
31037
- style: styles$f.errorText
31038
- }, lengthZeroStr), /*#__PURE__*/React__namespace.createElement(DefiningPointSettings, _extends({
31039
- label: "Point 1",
31040
- showPoint: showPoint1,
31041
- error: isInvalid ? lengthZeroStr : null
31042
- }, point1, {
31043
- onTogglePoint: newValue => onChangeProps({
31044
- showPoint1: newValue
31045
- }),
31046
- onChangeProps: newProps => handleChangePoint(newProps, 0)
31047
- })), /*#__PURE__*/React__namespace.createElement(DefiningPointSettings, _extends({
31048
- label: "Point 2",
31049
- showPoint: showPoint2,
31050
- error: isInvalid ? lengthZeroStr : null
31051
- }, point2, {
31052
- onTogglePoint: newValue => onChangeProps({
31053
- showPoint2: newValue
31054
- }),
31055
- onChangeProps: newProps => handleChangePoint(newProps, 1)
31056
- })), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
31057
- onRemove: onRemove,
31058
- figureAriaLabel: `locked line defined by
31059
- ${point1.coord[0]}, ${point1.coord[1]} and
31060
- ${point2.coord[0]}, ${point2.coord[1]}.`
31061
- }));
31077
+ }), showExpandButton && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
31078
+ kind: "secondary",
31079
+ onClick: () => toggleExpanded(allCollapsed),
31080
+ style: styles$f.button
31081
+ }, buttonLabel)));
31062
31082
  };
31063
31083
  const styles$f = aphrodite.StyleSheet.create({
31064
- row: {
31084
+ buttonContainer: {
31065
31085
  flexDirection: "row",
31066
31086
  alignItems: "center"
31067
31087
  },
31068
- spaceUnder: {
31069
- marginBottom: wonderBlocksTokens.spacing.xSmall_8
31070
- },
31071
- selectMarginOffset: {
31072
- // Align with the point settings accordions.
31073
- marginInlineEnd: -wonderBlocksTokens.spacing.xxxSmall_4
31074
- },
31075
- errorText: {
31076
- color: wonderBlocksTokens.color.red
31088
+ button: {
31089
+ marginTop: wonderBlocksTokens.spacing.xSmall_8,
31090
+ flexGrow: 1
31077
31091
  }
31078
31092
  });
31079
31093
 
31080
- /**
31081
- * LockedPointSettings is a component that allows the user to edit the
31082
- * settings of specifically a locked point on the graph.
31083
- *
31084
- * Used in the interactive graph editor's locked figures section.
31085
- */
31086
- const LockedPointSettings = props => {
31087
- const {
31088
- coord,
31089
- color: pointColor,
31090
- filled = true,
31091
- onChangeProps,
31092
- onRemove
31093
- } = props;
31094
- function handleColorChange(newValue) {
31095
- onChangeProps({
31096
- color: newValue
31097
- });
31094
+ const UNLIMITED = "unlimited";
31095
+ // Parses a string (ostensibly from some UI element that only supports string
31096
+ // types, such as a <select />) into a numeric point count or the special sigil
31097
+ // "unlimited". Note that 0 is a special case, and means "unlimited".
31098
+ const parsePointCount = points => {
31099
+ const parsed = parseInt(points, 10);
31100
+ if (isNaN(parsed)) {
31101
+ return UNLIMITED;
31098
31102
  }
31099
- return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
31100
- expanded: props.expanded,
31101
- onToggle: props.onToggle,
31102
- header:
31103
- /*#__PURE__*/
31104
- // Summary: Point, coords, color (filled/open)
31105
- React__namespace.createElement(wonderBlocksCore.View, {
31106
- style: styles$e.row
31107
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Point (${coord[0]}, ${coord[1]})`), /*#__PURE__*/React__namespace.createElement(Strut, {
31108
- size: wonderBlocksTokens.spacing.xSmall_8
31109
- }), /*#__PURE__*/React__namespace.createElement(ColorSwatch, {
31110
- color: pointColor,
31111
- filled: filled
31112
- }))
31113
- }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
31114
- coord: coord,
31115
- onChange: newCoord => {
31116
- onChangeProps({
31117
- coord: newCoord
31118
- });
31119
- }
31120
- }), /*#__PURE__*/React__namespace.createElement(ColorSelect, {
31121
- selectedValue: pointColor,
31122
- onChange: handleColorChange,
31123
- style: styles$e.spaceUnder
31124
- }), /*#__PURE__*/React__namespace.createElement(LabeledSwitch, {
31125
- label: "open point",
31126
- checked: !filled,
31103
+ return parsed === 0 ? UNLIMITED : parsed;
31104
+ };
31105
+
31106
+ const GraphPointsCountSelector = _ref => {
31107
+ let {
31108
+ numPoints = 1,
31109
+ onChange
31110
+ } = _ref;
31111
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
31112
+ selectedValue: `${numPoints}`,
31127
31113
  onChange: newValue => {
31128
- onChangeProps({
31129
- filled: !newValue
31130
- });
31114
+ onChange(parsePointCount(newValue));
31115
+ }
31116
+ // Never uses placeholder, always has value
31117
+ ,
31118
+ placeholder: "",
31119
+ style: styles$e.singleSelectShort
31120
+ }, [...[...Array(7).keys()].map(n => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31121
+ key: n,
31122
+ value: `${n}`,
31123
+ label: `${n} point${n > 1 ? "s" : ""}`
31124
+ })), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31125
+ key: "unlimited",
31126
+ value: UNLIMITED,
31127
+ label: "unlimited"
31128
+ })]);
31129
+ };
31130
+ const styles$e = aphrodite.StyleSheet.create({
31131
+ singleSelectShort: {
31132
+ // Non-standard spacing, but it's the smallest we can go
31133
+ // without running into styling issues with the dropdown.
31134
+ height: 26
31135
+ }
31136
+ });
31137
+
31138
+ const GraphTypeSelector = props => {
31139
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
31140
+ selectedValue: props.graphType,
31141
+ onChange: props.onChange,
31142
+ placeholder: "Select a graph type",
31143
+ style: styles$d.singleSelectShort
31144
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31145
+ value: "linear",
31146
+ label: "Linear function"
31147
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31148
+ value: "quadratic",
31149
+ label: "Quadratic function"
31150
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31151
+ value: "sinusoid",
31152
+ label: "Sinusoid function"
31153
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31154
+ value: "circle",
31155
+ label: "Circle"
31156
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31157
+ value: "point",
31158
+ label: "Point(s)"
31159
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31160
+ value: "linear-system",
31161
+ label: "Linear System"
31162
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31163
+ value: "polygon",
31164
+ label: "Polygon"
31165
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31166
+ value: "segment",
31167
+ label: "Line Segment(s)"
31168
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31169
+ value: "ray",
31170
+ label: "Ray"
31171
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31172
+ value: "angle",
31173
+ label: "Angle"
31174
+ }));
31175
+ };
31176
+ const styles$d = aphrodite.StyleSheet.create({
31177
+ singleSelectShort: {
31178
+ // Non-standard spacing, but it's the smallest we can go
31179
+ // without running into styling issues with the dropdown.
31180
+ height: 26
31181
+ }
31182
+ });
31183
+
31184
+ /* eslint-disable react/forbid-prop-types, react/no-unsafe */
31185
+ const {
31186
+ ButtonGroup: ButtonGroup$2,
31187
+ InfoTip: InfoTip$c,
31188
+ PropCheckBox: PropCheckBox$7,
31189
+ RangeInput: RangeInput$4
31190
+ } = perseus.components;
31191
+ const defaultBackgroundImage$1 = {
31192
+ url: null,
31193
+ width: 0,
31194
+ height: 0
31195
+ };
31196
+ function numSteps(range, step) {
31197
+ return Math.floor((range[1] - range[0]) / step);
31198
+ }
31199
+ function Heading(_ref) {
31200
+ let {
31201
+ title,
31202
+ isOpen,
31203
+ onToggle
31204
+ } = _ref;
31205
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31206
+ style: {
31207
+ flexDirection: "row",
31208
+ justifyContent: "space-between",
31209
+ backgroundColor: wonderBlocksTokens.color.offBlack8,
31210
+ padding: wonderBlocksTokens.spacing.xSmall_8,
31211
+ marginTop: wonderBlocksTokens.spacing.small_12,
31212
+ // NOTE(jeremy): This is the inverse of the @editorPadding CSS
31213
+ // variable found in perseus-editor.less. For now, it must
31214
+ // match otherwise there's a gap from this header to the edge
31215
+ // of the editor borders.
31216
+ marginInline: -10,
31217
+ cursor: "pointer"
31218
+ },
31219
+ onClick: () => onToggle?.(!isOpen)
31220
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelSmall, null, title), /*#__PURE__*/React__namespace.createElement(ToggleableCaret, {
31221
+ isExpanded: isOpen
31222
+ }));
31223
+ }
31224
+ class InteractiveGraphSettings extends React__namespace.Component {
31225
+ _isMounted = false;
31226
+ bgUrlRef = /*#__PURE__*/React__namespace.createRef();
31227
+ labelXRef = /*#__PURE__*/React__namespace.createRef();
31228
+ labelYRef = /*#__PURE__*/React__namespace.createRef();
31229
+ static stateFromProps(props) {
31230
+ return {
31231
+ labelsTextbox: props.labels,
31232
+ gridStepTextbox: props.gridStep,
31233
+ snapStepTextbox: props.snapStep,
31234
+ stepTextbox: props.step,
31235
+ rangeTextbox: props.range,
31236
+ // Shallow-copied clone
31237
+ backgroundImage: {
31238
+ ...props.backgroundImage
31239
+ }
31240
+ };
31241
+ }
31242
+ constructor(props) {
31243
+ super(props);
31244
+ this.state = {
31245
+ isExpanded: true,
31246
+ ...InteractiveGraphSettings.stateFromProps(props)
31247
+ };
31248
+ }
31249
+ static defaultProps = {
31250
+ box: [perseus.interactiveSizes.defaultBoxSizeSmall, perseus.interactiveSizes.defaultBoxSizeSmall],
31251
+ labels: ["x", "y"],
31252
+ range: [[-10, 10], [-10, 10]],
31253
+ step: [1, 1],
31254
+ gridStep: [1, 1],
31255
+ snapStep: [1, 1],
31256
+ valid: true,
31257
+ backgroundImage: defaultBackgroundImage$1,
31258
+ markings: "graph",
31259
+ showProtractor: false,
31260
+ showRuler: false,
31261
+ showTooltips: false,
31262
+ rulerLabel: "",
31263
+ rulerTicks: 10
31264
+ };
31265
+ componentDidMount() {
31266
+ // TODO(scottgrant): This is a hack to remove the deprecated call to
31267
+ // this.isMounted() but is still considered an anti-pattern.
31268
+ this._isMounted = true;
31269
+ this.changeGraph = ___default["default"].debounce(this.changeGraph, 300);
31270
+ }
31271
+ UNSAFE_componentWillReceiveProps(nextProps) {
31272
+ // Make sure that state updates when switching
31273
+ // between different items in a multi-item editor.
31274
+ if (!___default["default"].isEqual(this.props.labels, nextProps.labels) || !___default["default"].isEqual(this.props.gridStep, nextProps.gridStep) || !___default["default"].isEqual(this.props.snapStep, nextProps.snapStep) || !___default["default"].isEqual(this.props.step, nextProps.step) || !___default["default"].isEqual(this.props.range, nextProps.range) || !___default["default"].isEqual(this.props.backgroundImage, nextProps.backgroundImage)) {
31275
+ this.setState(InteractiveGraphSettings.stateFromProps(nextProps));
31131
31276
  }
31132
- }), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
31133
- onRemove: onRemove,
31134
- figureAriaLabel: `locked point at ${coord[0]}, ${coord[1]}`
31135
- }));
31136
- };
31137
- const styles$e = aphrodite.StyleSheet.create({
31138
- row: {
31139
- flexDirection: "row",
31140
- alignItems: "center"
31141
- },
31142
- spaceUnder: {
31143
- marginBottom: wonderBlocksTokens.spacing.xSmall_8
31144
31277
  }
31145
- });
31146
-
31147
- /**
31148
- * LockedVectorSettings is a component that allows the user to edit the
31149
- * settings of specifically a locked vector on the graph.
31150
- *
31151
- * Used in the interactive graph editor's locked figures section.
31152
- */
31153
- const lengthErrorMessage = "The vector cannot have length 0.";
31154
- const LockedVectorSettings = props => {
31155
- const {
31156
- points,
31157
- color: lineColor,
31158
- onChangeProps,
31159
- onRemove
31160
- } = props;
31161
- const [tail, tip] = points;
31162
- const lineLabel = `Vector (${tail[0]}, ${tail[1]}), (${tip[0]}, ${tip[1]})`;
31278
+ componentWillUnmount() {
31279
+ this._isMounted = false;
31280
+ }
31281
+ change = (() => {
31282
+ var _this = this;
31283
+ return function () {
31284
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
31285
+ args[_key] = arguments[_key];
31286
+ }
31287
+ return perseus.Changeable.change.apply(_this, args);
31288
+ };
31289
+ })();
31163
31290
 
31164
- // Check if the line has length 0.
31165
- const isInvalid = kmath.vector.equal(tail, tip);
31166
- function handleChangePoint(newCoord, index) {
31167
- if (typeof newCoord !== "undefined") {
31168
- const newPoints = [...points];
31169
- newPoints[index] = [...newCoord];
31170
- onChangeProps({
31171
- points: newPoints
31291
+ // TODO(aria): Make either a wrapper for standard events to work
31292
+ // with this.change, or make these use some TextInput/NumberInput box
31293
+ changeRulerLabel = e => {
31294
+ this.change({
31295
+ rulerLabel: e.target.value
31296
+ });
31297
+ };
31298
+ changeRulerTicks = e => {
31299
+ this.change({
31300
+ rulerTicks: +e.target.value
31301
+ });
31302
+ };
31303
+ changeBackgroundUrl = e => {
31304
+ // Only continue on blur or "enter"
31305
+ if (e.type === "keypress" && e.key !== "Enter") {
31306
+ return;
31307
+ }
31308
+ const setUrl = (url, width, height) => {
31309
+ // Shallow-copied clone
31310
+ const image = {
31311
+ ...this.props.backgroundImage
31312
+ };
31313
+ image.url = url;
31314
+ image.width = width;
31315
+ image.height = height;
31316
+ this.setState({
31317
+ backgroundImage: image
31318
+ }, this.changeGraph);
31319
+ };
31320
+ const url = this.bgUrlRef.current?.value;
31321
+ if (url) {
31322
+ perseus.Util.getImageSize(url, (width, height) => {
31323
+ if (this._isMounted) {
31324
+ setUrl(url, width, height);
31325
+ }
31172
31326
  });
31327
+ } else {
31328
+ setUrl(null, 0, 0);
31173
31329
  }
31174
- }
31175
- function handleColorChange(newColor) {
31176
- onChangeProps({
31177
- color: newColor
31330
+ };
31331
+ renderLabelChoices = choices => {
31332
+ return choices.map(nameAndValue => /*#__PURE__*/React__namespace.createElement("option", {
31333
+ key: nameAndValue[1],
31334
+ value: nameAndValue[1]
31335
+ }, nameAndValue[0]));
31336
+ };
31337
+ validRange = range => {
31338
+ const numbers = ___default["default"].every(range, function (num) {
31339
+ return ___default["default"].isFinite(num);
31178
31340
  });
31179
- }
31180
- return /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
31181
- expanded: props.expanded,
31182
- onToggle: props.onToggle,
31183
- header: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31184
- style: styles$d.row
31185
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, lineLabel), /*#__PURE__*/React__namespace.createElement(Strut, {
31186
- size: wonderBlocksTokens.spacing.xSmall_8
31187
- }), /*#__PURE__*/React__namespace.createElement(LineSwatch, {
31188
- color: lineColor,
31189
- lineStyle: "solid"
31190
- }))
31191
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31192
- style: [styles$d.row, styles$d.spaceUnder]
31193
- }, /*#__PURE__*/React__namespace.createElement(ColorSelect, {
31194
- selectedValue: lineColor,
31195
- onChange: handleColorChange
31196
- })), isInvalid && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
31197
- style: styles$d.errorText
31198
- }, lengthErrorMessage), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
31199
- expanded: true // Initial state is expanded
31200
- ,
31201
- containerStyle: styles$d.container,
31202
- panelStyle: styles$d.accordionPanel,
31203
- header: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31204
- style: styles$d.row
31205
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Tail (${tail[0]}, ${tail[1]})`))
31206
- }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
31207
- coord: tail,
31208
- error: isInvalid,
31209
- onChange: newProps => {
31210
- handleChangePoint(newProps, 0);
31341
+ if (!numbers) {
31342
+ return "Range must be a valid number";
31211
31343
  }
31212
- })), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsAccordion, {
31213
- expanded: true // Initial state is expanded
31214
- ,
31215
- containerStyle: styles$d.container,
31216
- panelStyle: styles$d.accordionPanel,
31217
- header: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31218
- style: styles$d.row
31219
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, null, `Tip (${tip[0]}, ${tip[1]})`))
31220
- }, /*#__PURE__*/React__namespace.createElement(CoordinatePairInput, {
31221
- coord: tip,
31222
- error: isInvalid,
31223
- onChange: newProps => {
31224
- handleChangePoint(newProps, 1);
31344
+ if (range[0] >= range[1]) {
31345
+ return "Range must have a higher number on the right";
31346
+ }
31347
+ return true;
31348
+ };
31349
+ validateStepValue = settings => {
31350
+ const {
31351
+ step,
31352
+ range,
31353
+ name,
31354
+ minTicks,
31355
+ maxTicks
31356
+ } = settings;
31357
+ const nSteps = numSteps(range, step);
31358
+ if (nSteps < minTicks) {
31359
+ return name + " is too large, there must be at least " + minTicks + " ticks.";
31360
+ }
31361
+ if (nSteps > maxTicks) {
31362
+ return name + " is too small, there can be at most " + maxTicks + " ticks.";
31363
+ }
31364
+ return true;
31365
+ };
31366
+ validSnapStep = (step, range) => {
31367
+ return this.validateStepValue({
31368
+ step: step,
31369
+ range: range,
31370
+ name: "Snap step",
31371
+ minTicks: 5,
31372
+ maxTicks: 60
31373
+ });
31374
+ };
31375
+ validGridStep = (step, range) => {
31376
+ return this.validateStepValue({
31377
+ step: step,
31378
+ range: range,
31379
+ name: "Grid step",
31380
+ minTicks: 3,
31381
+ maxTicks: 60
31382
+ });
31383
+ };
31384
+ validStep = (step, range) => {
31385
+ return this.validateStepValue({
31386
+ step: step,
31387
+ range: range,
31388
+ name: "Step",
31389
+ minTicks: 3,
31390
+ maxTicks: 20
31391
+ });
31392
+ };
31393
+ validBackgroundImageSize = image => {
31394
+ // Ignore empty images
31395
+ if (!image.url) {
31396
+ return true;
31397
+ }
31398
+ const validSize = image.width <= 450 && image.height <= 450;
31399
+ if (!validSize) {
31400
+ return "Image must be smaller than 450px x 450px.";
31401
+ }
31402
+ return true;
31403
+ };
31404
+ validateGraphSettings = (range, step, gridStep, snapStep, image) => {
31405
+ const self = this;
31406
+ let msg;
31407
+ const goodRange = ___default["default"].every(range, function (range) {
31408
+ msg = self.validRange(range);
31409
+ return msg === true;
31410
+ });
31411
+ if (!goodRange) {
31412
+ return msg;
31413
+ }
31414
+ const goodStep = ___default["default"].every(step, function (step, i) {
31415
+ msg = self.validStep(step, range[i]);
31416
+ return msg === true;
31417
+ });
31418
+ if (!goodStep) {
31419
+ return msg;
31420
+ }
31421
+ const goodGridStep = ___default["default"].every(gridStep, function (gridStep, i) {
31422
+ msg = self.validGridStep(gridStep, range[i]);
31423
+ return msg === true;
31424
+ });
31425
+ if (!goodGridStep) {
31426
+ return msg;
31427
+ }
31428
+ const goodSnapStep = ___default["default"].every(snapStep, function (snapStep, i) {
31429
+ msg = self.validSnapStep(snapStep, range[i]);
31430
+ return msg === true;
31431
+ });
31432
+ if (!goodSnapStep) {
31433
+ return msg;
31225
31434
  }
31226
- })), /*#__PURE__*/React__namespace.createElement(LockedFigureSettingsActions, {
31227
- onRemove: onRemove,
31228
- figureAriaLabel: `locked vector defined by starting point
31229
- ${tail[0]}, ${tail[1]} and extending to
31230
- ${tip[0]}, ${tip[1]}.`
31231
- }));
31232
- };
31233
- const styles$d = aphrodite.StyleSheet.create({
31234
- accordionPanel: {
31235
- paddingBottom: wonderBlocksTokens.spacing.medium_16
31236
- },
31237
- container: {
31238
- marginTop: wonderBlocksTokens.spacing.xSmall_8,
31239
- marginBottom: 0,
31240
- marginLeft: -wonderBlocksTokens.spacing.xxxSmall_4,
31241
- marginRight: -wonderBlocksTokens.spacing.xxxSmall_4,
31242
- backgroundColor: wonderBlocksTokens.color.white
31243
- },
31244
- errorText: {
31245
- color: wonderBlocksTokens.color.red,
31246
- marginTop: wonderBlocksTokens.spacing.xSmall_8
31247
- },
31248
- row: {
31249
- flexDirection: "row",
31250
- alignItems: "center"
31251
- }
31252
- });
31253
-
31254
- /**
31255
- * LockedFigureSettings takes in a figure and renders the
31256
- * respective settings for that figure type.
31257
- *
31258
- * Used in the interactive graph editor's locked figures section.
31259
- */
31260
-
31261
- // Union this type with other locked figure types when they are added.
31262
-
31263
- const LockedFigureSettings = props => {
31264
- switch (props.type) {
31265
- case "point":
31266
- return /*#__PURE__*/React__namespace.createElement(LockedPointSettings, props);
31267
- case "line":
31268
- return /*#__PURE__*/React__namespace.createElement(LockedLineSettings, props);
31269
- case "ellipse":
31270
- if (props.showM2Features) {
31271
- return /*#__PURE__*/React__namespace.createElement(LockedEllipseSettings, props);
31272
- }
31273
- break;
31274
- case "vector":
31275
- if (props.showM2Features) {
31276
- return /*#__PURE__*/React__namespace.createElement(LockedVectorSettings, props);
31435
+ const goodImageSize = this.validBackgroundImageSize(image);
31436
+ if (goodImageSize !== true) {
31437
+ msg = goodImageSize;
31438
+ return msg;
31439
+ }
31440
+ return true;
31441
+ };
31442
+ changeLabel = (i, e) => {
31443
+ const val = e.target.value;
31444
+ const labels = this.state.labelsTextbox.slice();
31445
+ labels[i] = val;
31446
+ this.setState({
31447
+ labelsTextbox: labels
31448
+ }, this.changeGraph);
31449
+ };
31450
+ changeRange = (i, values) => {
31451
+ const ranges = this.state.rangeTextbox.slice();
31452
+ ranges[i] = values;
31453
+ const step = this.state.stepTextbox.slice();
31454
+ const gridStep = this.state.gridStepTextbox.slice();
31455
+ const snapStep = this.state.snapStepTextbox.slice();
31456
+ const scale = perseus.Util.scaleFromExtent(ranges[i], this.props.box[i]);
31457
+ if (this.validRange(ranges[i]) === true) {
31458
+ step[i] = perseus.Util.tickStepFromExtent(ranges[i], this.props.box[i]);
31459
+ const gridStepValue = perseus.Util.gridStepFromTickStep(step[i], scale);
31460
+ if (gridStepValue) {
31461
+ gridStep[i] = gridStepValue;
31277
31462
  }
31278
- break;
31279
- }
31280
- return null;
31281
- };
31463
+ snapStep[i] = gridStep[i] / 2;
31464
+ }
31465
+ this.setState({
31466
+ stepTextbox: step,
31467
+ gridStepTextbox: gridStep,
31468
+ snapStepTextbox: snapStep,
31469
+ rangeTextbox: ranges
31470
+ }, this.changeGraph);
31471
+ };
31472
+ changeStep = step => {
31473
+ this.setState({
31474
+ stepTextbox: step
31475
+ }, this.changeGraph);
31476
+ };
31477
+ changeSnapStep = snapStep => {
31478
+ this.setState({
31479
+ snapStepTextbox: snapStep
31480
+ }, this.changeGraph);
31481
+ };
31482
+ changeGridStep = gridStep => {
31483
+ this.setState({
31484
+ gridStepTextbox: gridStep,
31485
+ snapStepTextbox: ___default["default"].map(gridStep, function (step) {
31486
+ return step / 2;
31487
+ })
31488
+ }, this.changeGraph);
31489
+ };
31490
+ changeGraph = () => {
31491
+ const labels = this.state.labelsTextbox;
31492
+ const range = ___default["default"].map(this.state.rangeTextbox, function (range) {
31493
+ return ___default["default"].map(range, Number);
31494
+ });
31495
+ const step = ___default["default"].map(this.state.stepTextbox, Number);
31496
+ const gridStep = this.state.gridStepTextbox;
31497
+ const snapStep = this.state.snapStepTextbox;
31498
+ const image = this.state.backgroundImage;
31282
31499
 
31283
- function focusWithChromeStickyFocusBugWorkaround(element) {
31284
- // NOTE(marksandstrom) Chrome sticky focus bug.
31285
- //
31286
- // There is a bug in Chrome related to sticky positioning and
31287
- // focus: When a child of an element with sticky positioning is
31288
- // focused with the focus() method, the scrollable area
31289
- // containing that element will scroll to the top (even though
31290
- // the element is already visble on the page). For a dropdown
31291
- // this means that the area will be scrolled to the top when
31292
- // the user interacts with the dropdown (since we actively
31293
- // manage dropdown header and option focus for keyboard users).
31294
- //
31295
- // To work around this bug the `preventScroll` focus option is
31296
- // used.
31297
- //
31298
- // Using `focusOptions` is an experimental browser feature
31299
- // "that should not be used in production code". However, since
31300
- // Chrome is the only browser we support that exhibits this
31301
- // buggy behavior and using `focusOptions` works around the
31302
- // bug, it is perfectly reasonable to use the feature in this
31303
- // case. We are not relying on the effect of the
31304
- // `preventScroll` option otherwise.
31305
- //
31306
- // focusOptions documentation:
31307
- //
31308
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
31309
- //
31310
- // Note that there could be cases where we want default focus() behavior.
31311
- // One that comes to mind is a dropdown with a long list of options that
31312
- // might be rendered off screen when opened. With this workaround the
31313
- // selected option would not be scrolled into view if the option is near
31314
- // the end of the list. We're not aware of any uses of the dropdown where
31315
- // this is an issue, however, so there isn't currently a way to out of the
31316
- // `preventScroll` behavior.
31500
+ // validationResult is either:
31501
+ // true -> the settings are valid
31502
+ // a string -> the settings are invalid, and the explanation
31503
+ // is contained in the string
31504
+ // TODO(aria): Refactor this to not be confusing
31505
+ const validationResult = this.validateGraphSettings(range, step, gridStep, snapStep, image);
31506
+ if (validationResult === true) {
31507
+ // either true or a string
31508
+ this.change({
31509
+ valid: true,
31510
+ labels: labels,
31511
+ range: range,
31512
+ step: step,
31513
+ gridStep: gridStep,
31514
+ snapStep: snapStep,
31515
+ backgroundImage: image
31516
+ });
31517
+ } else {
31518
+ this.change({
31519
+ valid: validationResult // a string message, not false
31520
+ });
31521
+ }
31522
+ };
31317
31523
 
31318
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element'.
31319
- element.focus({
31320
- preventScroll: true
31321
- });
31322
- }
31323
- const DEFAULT_COLOR = "grayH";
31324
- function getDefaultFigureForType(type) {
31325
- switch (type) {
31326
- case "point":
31327
- return {
31328
- type: "point",
31329
- coord: [0, 0],
31330
- color: DEFAULT_COLOR,
31331
- filled: true
31332
- };
31333
- case "line":
31334
- return {
31335
- type: "line",
31336
- kind: "line",
31337
- points: [getDefaultFigureForType("point"), {
31338
- ...getDefaultFigureForType("point"),
31339
- coord: [2, 2]
31340
- }],
31341
- color: DEFAULT_COLOR,
31342
- lineStyle: "solid",
31343
- showPoint1: false,
31344
- showPoint2: false
31345
- };
31346
- case "ellipse":
31347
- return {
31348
- type: "ellipse",
31349
- center: [0, 0],
31350
- radius: [1, 1],
31351
- angle: 0,
31352
- color: DEFAULT_COLOR,
31353
- fillStyle: "none",
31354
- strokeStyle: "solid"
31355
- };
31356
- case "vector":
31357
- return {
31358
- type: "vector",
31359
- points: [[0, 0], [2, 2]],
31360
- color: DEFAULT_COLOR
31361
- };
31362
- default:
31363
- throw new wonderStuffCore.UnreachableCaseError(type);
31524
+ render() {
31525
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(Heading, {
31526
+ title: "Common Graph Settings",
31527
+ isOpen: this.state.isExpanded,
31528
+ onToggle: () => this.setState({
31529
+ isExpanded: !this.state.isExpanded
31530
+ })
31531
+ }), this.state.isExpanded && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement("div", {
31532
+ className: "graph-settings"
31533
+ }, /*#__PURE__*/React__namespace.createElement("div", {
31534
+ className: "perseus-widget-row"
31535
+ }, /*#__PURE__*/React__namespace.createElement("div", {
31536
+ className: "perseus-widget-left-col"
31537
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31538
+ label: "x Label"
31539
+ }, /*#__PURE__*/React__namespace.createElement("input", {
31540
+ type: "text",
31541
+ className: "graph-settings-axis-label",
31542
+ ref: this.labelXRef,
31543
+ onChange: e => this.changeLabel(0, e),
31544
+ value: this.state.labelsTextbox[0] || ""
31545
+ }))), /*#__PURE__*/React__namespace.createElement("div", {
31546
+ className: "perseus-widget-right-col"
31547
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31548
+ label: "y Label"
31549
+ }, /*#__PURE__*/React__namespace.createElement("input", {
31550
+ type: "text",
31551
+ className: "graph-settings-axis-label",
31552
+ ref: this.labelYRef,
31553
+ onChange: e => this.changeLabel(1, e),
31554
+ value: this.state.labelsTextbox[1] || ""
31555
+ })))), /*#__PURE__*/React__namespace.createElement("div", {
31556
+ className: "perseus-widget-row"
31557
+ }, /*#__PURE__*/React__namespace.createElement("div", {
31558
+ className: "perseus-widget-left-col"
31559
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31560
+ label: "x Range"
31561
+ }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
31562
+ value: this.state.rangeTextbox[0],
31563
+ onChange: vals => this.changeRange(0, vals)
31564
+ }))), /*#__PURE__*/React__namespace.createElement("div", {
31565
+ className: "perseus-widget-right-col"
31566
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31567
+ label: "y Range"
31568
+ }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
31569
+ value: this.state.rangeTextbox[1],
31570
+ onChange: vals => this.changeRange(1, vals)
31571
+ })))), /*#__PURE__*/React__namespace.createElement("div", {
31572
+ className: "perseus-widget-row"
31573
+ }, /*#__PURE__*/React__namespace.createElement("div", {
31574
+ className: "perseus-widget-left-col"
31575
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31576
+ label: "Tick Step"
31577
+ }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
31578
+ value: this.state.stepTextbox,
31579
+ onChange: this.changeStep
31580
+ }))), /*#__PURE__*/React__namespace.createElement("div", {
31581
+ className: "perseus-widget-right-col"
31582
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31583
+ label: "Grid Step"
31584
+ }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
31585
+ value: this.state.gridStepTextbox,
31586
+ onChange: this.changeGridStep
31587
+ })))), /*#__PURE__*/React__namespace.createElement("div", {
31588
+ className: "perseus-widget-row"
31589
+ }, /*#__PURE__*/React__namespace.createElement("div", {
31590
+ className: "perseus-widget-left-col"
31591
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31592
+ label: "Snap Step"
31593
+ }, /*#__PURE__*/React__namespace.createElement(RangeInput$4, {
31594
+ value: this.state.snapStepTextbox,
31595
+ onChange: this.changeSnapStep
31596
+ })))), /*#__PURE__*/React__namespace.createElement("div", {
31597
+ className: "perseus-widget-row"
31598
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31599
+ label: "Markings:"
31600
+ }, /*#__PURE__*/React__namespace.createElement(ButtonGroup$2, {
31601
+ value: this.props.markings,
31602
+ allowEmpty: false,
31603
+ buttons: [{
31604
+ value: "graph",
31605
+ content: "Graph"
31606
+ }, {
31607
+ value: "grid",
31608
+ content: "Grid"
31609
+ }, {
31610
+ value: "none",
31611
+ content: "None"
31612
+ }],
31613
+ onChange: this.change("markings")
31614
+ }))), /*#__PURE__*/React__namespace.createElement("div", {
31615
+ className: "perseus-widget-left-col"
31616
+ }, /*#__PURE__*/React__namespace.createElement(PropCheckBox$7, {
31617
+ label: "Show tooltips",
31618
+ showTooltips: this.props.showTooltips,
31619
+ onChange: this.change
31620
+ }))), /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31621
+ label: "Background image URL:",
31622
+ style: styles$c.resetSpaceTop
31623
+ }, /*#__PURE__*/React__namespace.createElement("input", {
31624
+ type: "text",
31625
+ className: aphrodite.css(styles$c.backgroundUrlInput),
31626
+ ref: this.bgUrlRef,
31627
+ value: this.state.backgroundImage.url || "",
31628
+ onChange: e => {
31629
+ // Shallow-copied clone
31630
+ const image = {
31631
+ ...this.props.backgroundImage
31632
+ };
31633
+ image.url = e.target.value;
31634
+ this.setState({
31635
+ backgroundImage: image
31636
+ });
31637
+ },
31638
+ onKeyPress: this.changeBackgroundUrl,
31639
+ onBlur: this.changeBackgroundUrl
31640
+ }), /*#__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, {
31641
+ style: styles$c.rulerSection
31642
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31643
+ style: styles$c.checkboxRow
31644
+ }, /*#__PURE__*/React__namespace.createElement(PropCheckBox$7, {
31645
+ label: "Show ruler",
31646
+ showRuler: this.props.showRuler,
31647
+ onChange: this.change,
31648
+ style: styles$c.resetSpaceTop
31649
+ }), /*#__PURE__*/React__namespace.createElement(PropCheckBox$7, {
31650
+ label: "Show protractor",
31651
+ showProtractor: this.props.showProtractor,
31652
+ onChange: this.change,
31653
+ style: styles$c.resetSpaceTop
31654
+ })), (this.props.showRuler || this.props.showProtractor) && /*#__PURE__*/React__namespace.createElement(Banner__default["default"], {
31655
+ layout: "floating",
31656
+ text: "The ruler and protractor are not accessible. Please consider an alternate approach.",
31657
+ kind: "warning"
31658
+ }), this.props.showRuler && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31659
+ style: styles$c.spaceTop
31660
+ }, /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31661
+ label: "Ruler label:",
31662
+ style: styles$c.resetSpaceTop
31663
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
31664
+ id: "ruler-label-select",
31665
+ selectedValue: this.props.rulerLabel,
31666
+ onChange: newValue => {
31667
+ this.change({
31668
+ rulerLabel: newValue
31669
+ });
31670
+ },
31671
+ placeholder: "None",
31672
+ style: styles$c.singleSelectShort
31673
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31674
+ value: "",
31675
+ label: "None",
31676
+ horizontalRule: "full-width"
31677
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31678
+ value: "mm",
31679
+ label: "Milimeters"
31680
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31681
+ value: "cm",
31682
+ label: "Centimeters"
31683
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31684
+ value: "m",
31685
+ label: "Meters"
31686
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31687
+ value: "km",
31688
+ label: "Kilometers",
31689
+ horizontalRule: "full-width"
31690
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31691
+ value: "in",
31692
+ label: "Inches"
31693
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31694
+ value: "ft",
31695
+ label: "Feet"
31696
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31697
+ value: "yd",
31698
+ label: "Yards"
31699
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31700
+ value: "mi",
31701
+ label: "Miles"
31702
+ }))), /*#__PURE__*/React__namespace.createElement(LabeledRow, {
31703
+ label: "Ruler ticks:"
31704
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
31705
+ id: "ruler-ticks-select",
31706
+ selectedValue: `${this.props.rulerTicks}`,
31707
+ onChange: newValue => {
31708
+ this.change({
31709
+ rulerTicks: newValue
31710
+ });
31711
+ },
31712
+ placeholder: "10",
31713
+ style: styles$c.singleSelectShort
31714
+ }, [1, 2, 4, 8, 10, 16].map(value => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
31715
+ key: value,
31716
+ value: `${value}`,
31717
+ label: `${value}`
31718
+ }))))))));
31364
31719
  }
31365
31720
  }
31366
-
31367
- const LockedFiguresSection = props => {
31368
- // Keep track of all figures' accordions' expanded states for the
31369
- // expand/collapse all button. Set the whole array to false initially.
31370
- const collapsedStateArray = Array((props.figures ?? []).length).fill(false);
31371
- const [expandedStates, setExpandedStates] = React__namespace.useState(collapsedStateArray);
31372
- const uniqueId = wonderBlocksCore.useUniqueIdWithMock().get("locked-figures-section");
31373
- const {
31374
- figures,
31375
- onChange
31376
- } = props;
31377
- function addLockedFigure(newFigure) {
31378
- const lockedFigures = figures || [];
31379
- const newProps = {
31380
- lockedFigures: [...lockedFigures, getDefaultFigureForType(newFigure)]
31381
- };
31382
- onChange(newProps);
31383
- setExpandedStates([...expandedStates, true]);
31384
- }
31385
- function removeLockedFigure(index) {
31386
- // eslint-disable-next-line no-alert
31387
- if (window.confirm("Are you sure you want to delete this figure?")) {
31388
- const lockedFigures = figures || [];
31389
- onChange({
31390
- lockedFigures: [...lockedFigures.slice(0, index), ...lockedFigures.slice(index + 1)]
31391
- });
31392
-
31393
- // Update expanded states
31394
- const newExpandedStates = [...expandedStates];
31395
- newExpandedStates.splice(index, 1);
31396
- setExpandedStates(newExpandedStates);
31397
- }
31398
- }
31399
- function changeProps(index,
31400
- // Omit the type from the figure props so it doesn't think
31401
- // we're trying to pass in the props for the wrong type.
31402
- figureProps) {
31403
- const lockedFigures = figures || [];
31404
- const newFigures = {
31405
- lockedFigures: [...lockedFigures.slice(0, index), {
31406
- ...lockedFigures[index],
31407
- ...figureProps
31408
- }, ...lockedFigures.slice(index + 1)]
31409
- };
31410
- onChange(newFigures);
31411
- }
31412
- function toggleExpanded(newValue) {
31413
- setExpandedStates(Array(figures?.length).fill(newValue));
31414
- }
31415
- const allCollapsed = expandedStates.every(value => !value);
31416
- const buttonLabel = allCollapsed ? "Expand all" : "Collapse all";
31417
- const showExpandButton = !!figures?.length;
31418
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, figures?.map((figure, index) => {
31419
- return /*#__PURE__*/React__namespace.createElement(LockedFigureSettings, _extends({
31420
- key: `${uniqueId}-locked-${figure}-${index}`,
31421
- showM2Features: props.showM2Features,
31422
- expanded: expandedStates[index],
31423
- onToggle: newValue => {
31424
- const newExpanded = [...expandedStates];
31425
- newExpanded[index] = newValue;
31426
- setExpandedStates(newExpanded);
31427
- }
31428
- }, figure, {
31429
- onChangeProps: newProps => changeProps(index, newProps),
31430
- onRemove: () => removeLockedFigure(index)
31431
- }));
31432
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31433
- style: styles$c.buttonContainer
31434
- }, /*#__PURE__*/React__namespace.createElement(LockedFigureSelect, {
31435
- showM2Features: props.showM2Features,
31436
- id: `${uniqueId}-select`,
31437
- onChange: addLockedFigure
31438
- }), /*#__PURE__*/React__namespace.createElement(Strut, {
31439
- size: wonderBlocksTokens.spacing.small_12
31440
- }), showExpandButton && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
31441
- kind: "secondary",
31442
- onClick: () => toggleExpanded(allCollapsed),
31443
- style: styles$c.button
31444
- }, buttonLabel)));
31445
- };
31446
31721
  const styles$c = aphrodite.StyleSheet.create({
31447
- buttonContainer: {
31722
+ resetSpaceTop: {
31723
+ marginTop: 0
31724
+ },
31725
+ spaceTop: {
31726
+ marginTop: wonderBlocksTokens.spacing.xSmall_8
31727
+ },
31728
+ singleSelectShort: {
31729
+ // Non-standard spacing, but it's the smallest we can go
31730
+ // without running into styling issues with the dropdown.
31731
+ height: 26
31732
+ },
31733
+ backgroundUrlInput: {
31734
+ border: `1px solid ${wonderBlocksTokens.color.offBlack32}`,
31735
+ borderRadius: wonderBlocksTokens.spacing.xxxSmall_4,
31736
+ padding: wonderBlocksTokens.spacing.xxxSmall_4
31737
+ },
31738
+ checkboxRow: {
31448
31739
  flexDirection: "row",
31449
- alignItems: "center"
31740
+ alignItems: "center",
31741
+ justifyContent: "space-between",
31742
+ marginBottom: wonderBlocksTokens.spacing.xSmall_8
31450
31743
  },
31451
- button: {
31744
+ rulerSection: {
31452
31745
  marginTop: wonderBlocksTokens.spacing.xSmall_8,
31453
- flexGrow: 1
31746
+ borderTop: `1px solid ${wonderBlocksTokens.color.offBlack16}`,
31747
+ paddingTop: wonderBlocksTokens.spacing.xSmall_8,
31748
+ paddingBottom: wonderBlocksTokens.spacing.xSmall_8,
31749
+ borderBottom: `1px solid ${wonderBlocksTokens.color.offBlack16}`
31454
31750
  }
31455
31751
  });
31456
31752
 
@@ -31538,6 +31834,15 @@ class InteractiveGraphEditor extends React__namespace.Component {
31538
31834
  UNSAFE_componentWillMount() {
31539
31835
  DeprecationMixin.UNSAFE_componentWillMount.call(this);
31540
31836
  }
31837
+ static buildGraphKey(correct) {
31838
+ const testGraphKey = [];
31839
+ for (const key in correct) {
31840
+ if (correct[key]) {
31841
+ typeof correct[key] === "number" || typeof correct[key] === "string" || typeof correct[key] === "boolean" ? testGraphKey.push(correct[key]) : testGraphKey.push(0);
31842
+ }
31843
+ }
31844
+ return testGraphKey.join(":");
31845
+ }
31541
31846
  render() {
31542
31847
  let graph;
31543
31848
  let equationString;
@@ -31580,10 +31885,9 @@ class InteractiveGraphEditor extends React__namespace.Component {
31580
31885
  });
31581
31886
  }
31582
31887
  };
31583
-
31584
31888
  // This is used to force a remount of the graph component
31585
31889
  // when there's a significant change
31586
- const graphKey = `${correct.type}:${correct.numSegments || 0}`;
31890
+ const graphKey = InteractiveGraphEditor.buildGraphKey(correct);
31587
31891
  graph =
31588
31892
  /*#__PURE__*/
31589
31893
  // There are a bunch of props that renderer.jsx passes to widgets via
@@ -31796,6 +32100,7 @@ class InteractiveGraphEditor extends React__namespace.Component {
31796
32100
  // is using Mafs.
31797
32101
  this.props.graph && this.props.apiOptions?.flags?.mafs?.[this.props.graph.type] && /*#__PURE__*/React__namespace.createElement(LockedFiguresSection, {
31798
32102
  showM2Features: this.props.apiOptions?.flags?.mafs?.["interactive-graph-locked-features-m2"],
32103
+ showM2bFeatures: this.props.apiOptions?.flags?.mafs?.["interactive-graph-locked-features-m2b"],
31799
32104
  figures: this.props.lockedFigures,
31800
32105
  onChange: this.props.onChange
31801
32106
  }));