@khanacademy/perseus-editor 2.2.2 → 2.3.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 (48) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/all-widgets.d.ts +2 -2
  3. package/dist/all-widgets.js.flow +2 -2
  4. package/dist/components/graph-points-count-selector.d.ts +7 -0
  5. package/dist/components/graph-points-count-selector.js.flow +12 -0
  6. package/dist/components/graph-type-selector.d.ts +7 -0
  7. package/dist/components/graph-type-selector.js.flow +12 -0
  8. package/dist/components/segment-count-selector.d.ts +6 -0
  9. package/dist/components/segment-count-selector.js.flow +11 -0
  10. package/dist/components/widget-editor.d.ts +31 -0
  11. package/dist/components/widget-editor.js.flow +91 -0
  12. package/dist/components/widget-select.d.ts +10 -0
  13. package/dist/components/widget-select.js.flow +16 -0
  14. package/dist/es/index.js +423 -229
  15. package/dist/es/index.js.map +1 -1
  16. package/dist/index.js +431 -225
  17. package/dist/index.js.map +1 -1
  18. package/dist/testing-widgets.d.ts +2 -2
  19. package/dist/testing-widgets.js.flow +2 -2
  20. package/dist/util/points.d.ts +3 -0
  21. package/dist/util/points.js.flow +9 -0
  22. package/dist/widgets/example-graphie-widget.d.ts +2 -2
  23. package/dist/widgets/example-graphie-widget.js.flow +2 -2
  24. package/dist/widgets/example-widget.d.ts +2 -2
  25. package/dist/widgets/example-widget.js.flow +2 -2
  26. package/dist/widgets/interactive-graph-editor.d.ts +3182 -1
  27. package/dist/widgets/interactive-graph-editor.js.flow +5290 -1
  28. package/package.json +9 -3
  29. package/src/__stories__/editor.stories.tsx +105 -4
  30. package/src/article-editor.tsx +1 -1
  31. package/src/{__stories__ → components/__stories__}/section-control-button.stories.tsx +1 -1
  32. package/src/components/graph-points-count-selector.tsx +29 -0
  33. package/src/components/graph-type-selector.tsx +31 -0
  34. package/src/components/json-editor.tsx +0 -1
  35. package/src/components/segment-count-selector.tsx +27 -0
  36. package/src/components/widget-editor.tsx +232 -0
  37. package/src/components/widget-select.tsx +48 -0
  38. package/src/editor.tsx +4 -270
  39. package/src/util/points.test.ts +24 -0
  40. package/src/util/points.ts +13 -0
  41. package/src/widgets/interactive-graph-editor.tsx +294 -48
  42. package/tsconfig-build.json +0 -1
  43. package/tsconfig-build.tsbuildinfo +1 -1
  44. package/dist/diffs/__stories__/perseus-diff-wrapper.d.ts +0 -6
  45. package/dist/diffs/__stories__/perseus-diff-wrapper.js.flow +0 -12
  46. /package/dist/{section-control-button.d.ts → components/section-control-button.d.ts} +0 -0
  47. /package/dist/{section-control-button.js.flow → components/section-control-button.js.flow} +0 -0
  48. /package/src/{section-control-button.tsx → components/section-control-button.tsx} +0 -0
package/dist/es/index.js CHANGED
@@ -1,17 +1,20 @@
1
- import { constants, components, Widgets, Util, Log, PerseusError, Errors, PerseusMarkdown, preprocessTex, icons, WIDGET_PROP_DENYLIST, ApiOptions, buildMapper, itemToTree, buildEmptyItemTreeForShape, shapes, itemDataVersion, ClassNames, MultiItems, traverse, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, WidgetJsonifyDeprecated, Expression, interactiveSizes, KhanMath, GrapherWidget, GrapherUtil, SizingUtils, KhanColors, ColorPicker, DashPicker, ArrowPicker, ElementContainer, InteractiveGraphWidget, globalStyles, globalConstants, MatrixWidget, PlotterWidget, BaseRadio, TableWidget, TransformerWidget, displaySigFigs, widgets } from '@khanacademy/perseus';
1
+ import { constants, components, Widgets, icons, WIDGET_PROP_DENYLIST, Util, Log, PerseusError, Errors, PerseusMarkdown, preprocessTex, ApiOptions, buildMapper, itemToTree, buildEmptyItemTreeForShape, shapes, itemDataVersion, ClassNames, MultiItems, traverse, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, WidgetJsonifyDeprecated, Expression, interactiveSizes, KhanMath, GrapherWidget, GrapherUtil, SizingUtils, KhanColors, ColorPicker, DashPicker, ArrowPicker, ElementContainer, InteractiveGraphWidget, globalStyles, globalConstants, MatrixWidget, PlotterWidget, BaseRadio, TableWidget, TransformerWidget, displaySigFigs, widgets } from '@khanacademy/perseus';
2
2
  import * as React from 'react';
3
3
  import _ from 'underscore';
4
4
  import PropTypes from 'prop-types';
5
5
  import createReactClass from 'create-react-class';
6
+ import Clickable from '@khanacademy/wonder-blocks-clickable';
7
+ import Color from '@khanacademy/wonder-blocks-color';
8
+ import { StyleSheet, css } from 'aphrodite';
6
9
  import $ from 'jquery';
7
10
  import * as ReactDOM from 'react-dom';
8
11
  import ReactDOM__default from 'react-dom';
9
- import { StyleSheet, css } from 'aphrodite';
10
- import Clickable from '@khanacademy/wonder-blocks-clickable';
11
- import Color from '@khanacademy/wonder-blocks-color';
12
12
  import classNames from 'classnames';
13
13
  import { point, number, line, ray, vector } from '@khanacademy/kmath';
14
14
  import * as KAS from '@khanacademy/kas';
15
+ import { View } from '@khanacademy/wonder-blocks-core';
16
+ import Spacing from '@khanacademy/wonder-blocks-spacing';
17
+ import * as Typography from '@khanacademy/wonder-blocks-typography';
15
18
  import Button from '@khanacademy/wonder-blocks-button';
16
19
  import * as i18n$1 from '@khanacademy/wonder-blocks-i18n';
17
20
 
@@ -96,7 +99,7 @@ DeviceFramer.propTypes = {
96
99
  nochrome: PropTypes.bool
97
100
  };
98
101
 
99
- /* eslint-disable react/prop-types */
102
+ /* eslint-disable @babel/no-invalid-this */
100
103
  const JsonEditor = createReactClass({
101
104
  displayName: "JsonEditor",
102
105
  getInitialState: function () {
@@ -190,6 +193,43 @@ const JsonEditor = createReactClass({
190
193
  }
191
194
  });
192
195
 
196
+ const {
197
+ InlineIcon: InlineIcon$c
198
+ } = components;
199
+ class SectionControlButton extends React.Component {
200
+ render() {
201
+ const {
202
+ icon,
203
+ onClick,
204
+ title
205
+ } = this.props;
206
+ return /*#__PURE__*/React.createElement(Clickable, {
207
+ className: "section-control-button " + "simple-button " + "simple-button--small ",
208
+ onClick: e => {
209
+ e.preventDefault();
210
+ onClick();
211
+ },
212
+ role: "button",
213
+ hideDefaultFocusRing: true,
214
+ "aria-label": title,
215
+ style: styles$f.button
216
+ }, ({
217
+ hovered,
218
+ focused,
219
+ pressed
220
+ }) => /*#__PURE__*/React.createElement(InlineIcon$c, icon));
221
+ }
222
+ }
223
+ const styles$f = StyleSheet.create({
224
+ button: {
225
+ backgroundColor: Color.gold,
226
+ margin: 2,
227
+ paddingRight: 10,
228
+ paddingLeft: 10,
229
+ paddingBottom: 2
230
+ }
231
+ });
232
+
193
233
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
194
234
 
195
235
  function getDefaultExportFromCjs (x) {
@@ -17683,115 +17723,6 @@ const DragTarget = createReactClass({
17683
17723
  }
17684
17724
  });
17685
17725
 
17686
- /**
17687
- * Displays a collapsable list of KaTeX rendering errors.
17688
- */
17689
- class KatexErrorView extends React.Component {
17690
- constructor(...args) {
17691
- super(...args);
17692
- this.state = {
17693
- showErrors: false
17694
- };
17695
- this.handleToggleKatexErrors = e => {
17696
- this.setState({
17697
- showErrors: !this.state.showErrors
17698
- });
17699
- };
17700
- }
17701
- render() {
17702
- const {
17703
- errorList
17704
- } = this.props;
17705
- const {
17706
- showErrors
17707
- } = this.state;
17708
-
17709
- // TODO(riley) replace with SVG icons
17710
- const disclosureClass = showErrors ? "icon-chevron-down" : "icon-chevron-right";
17711
- return /*#__PURE__*/React.createElement("div", {
17712
- className: css(styles$f.errorContainer)
17713
- }, /*#__PURE__*/React.createElement("div", {
17714
- className: css(styles$f.title),
17715
- onClick: this.handleToggleKatexErrors
17716
- }, /*#__PURE__*/React.createElement("i", {
17717
- className: disclosureClass,
17718
- style: {
17719
- fontSize: 14
17720
- }
17721
- }), "\xA0 KaTeX Errors (", errorList.length, ")"), showErrors && /*#__PURE__*/React.createElement("div", {
17722
- className: css(styles$f.errorExplanation)
17723
- }, "These errors will cause your LaTeX to load really slowly for the student. Please fix them if you can. If you can\u2019t because KaTeX doesn\u2019t support the feature you need, please message Cam."), showErrors && errorList.map((e, index) => /*#__PURE__*/React.createElement("div", {
17724
- className: css(styles$f.error),
17725
- key: index
17726
- }, /*#__PURE__*/React.createElement("div", {
17727
- style: {
17728
- color: "red"
17729
- }
17730
- }, e.math), /*#__PURE__*/React.createElement("div", null, e.message))));
17731
- }
17732
- }
17733
- KatexErrorView.propTypes = {
17734
- errorList: PropTypes.arrayOf(PropTypes.shape({
17735
- math: PropTypes.string.isRequired,
17736
- message: PropTypes.string.isRequired
17737
- })).isRequired
17738
- };
17739
- const styles$f = StyleSheet.create({
17740
- title: {
17741
- backgroundColor: "#eee",
17742
- fontSize: "1.25em",
17743
- padding: "4px 10px"
17744
- },
17745
- errorContainer: {
17746
- border: "1px solid #ddd",
17747
- borderTop: "none"
17748
- },
17749
- errorExplanation: {
17750
- padding: "4px 10px",
17751
- backgroundColor: "pink"
17752
- },
17753
- error: {
17754
- padding: "4px 10px"
17755
- }
17756
- });
17757
-
17758
- const {
17759
- InlineIcon: InlineIcon$c
17760
- } = components;
17761
- class SectionControlButton extends React.Component {
17762
- render() {
17763
- const {
17764
- icon,
17765
- onClick,
17766
- title
17767
- } = this.props;
17768
- return /*#__PURE__*/React.createElement(Clickable, {
17769
- className: "section-control-button " + "simple-button " + "simple-button--small ",
17770
- onClick: e => {
17771
- e.preventDefault();
17772
- onClick();
17773
- },
17774
- role: "button",
17775
- hideDefaultFocusRing: true,
17776
- "aria-label": title,
17777
- style: styles$e.button
17778
- }, ({
17779
- hovered,
17780
- focused,
17781
- pressed
17782
- }) => /*#__PURE__*/React.createElement(InlineIcon$c, icon));
17783
- }
17784
- }
17785
- const styles$e = StyleSheet.create({
17786
- button: {
17787
- backgroundColor: Color.gold,
17788
- margin: 2,
17789
- paddingRight: 10,
17790
- paddingLeft: 10,
17791
- paddingBottom: 2
17792
- }
17793
- });
17794
-
17795
17726
  const {
17796
17727
  InlineIcon: InlineIcon$b,
17797
17728
  PropCheckBox: PropCheckBox$e
@@ -17801,80 +17732,6 @@ const {
17801
17732
  iconChevronRight,
17802
17733
  iconTrash: iconTrash$8
17803
17734
  } = icons;
17804
-
17805
- // like [[snowman input-number 1]]
17806
- const widgetPlaceholder = "[[\u2603 {id}]]";
17807
- const widgetRegExp = "(\\[\\[\u2603 {id}\\]\\])";
17808
- const rWidgetSplit = new RegExp(widgetRegExp.replace("{id}", "[a-z-]+ [0-9]+"), "g");
17809
- const shortcutRegexp = /^\[\[([a-z-]+)$/; // like [[nu, [[int, etc
17810
-
17811
- const ENDS_WITH_A_PARAGRAPH = /(?:\n{2,}|^\n*)$/;
17812
- const TRAILING_NEWLINES = /(\n*)$/;
17813
- const LEADING_NEWLINES = /^(\n*)/;
17814
- const commafyInteger = n => {
17815
- let str = n.toString();
17816
- if (str.length >= 5) {
17817
- str = str.replace(/(\d)(?=(\d{3})+$)/g, "$1{,}");
17818
- }
17819
- return str;
17820
- };
17821
- const makeEndWithAParagraphIfNecessary = content => {
17822
- if (!ENDS_WITH_A_PARAGRAPH.test(content)) {
17823
- const match = TRAILING_NEWLINES.exec(content);
17824
- if (match) {
17825
- const newlines = match[1];
17826
- return content + "\n\n".slice(0, 2 - newlines.length);
17827
- }
17828
- }
17829
- return content;
17830
- };
17831
- const makeStartWithAParagraphAlways = content => {
17832
- const match = LEADING_NEWLINES.exec(content);
17833
- if (!match) {
17834
- return content;
17835
- }
17836
- const newlines = match[1];
17837
- return "\n\n".slice(0, 2 - newlines.length) + content;
17838
- };
17839
- class WidgetSelect extends React.Component {
17840
- constructor(...args) {
17841
- super(...args);
17842
- this.handleChange = e => {
17843
- const widgetType = e.currentTarget.value;
17844
- if (widgetType === "") {
17845
- // TODO(alpert): Not sure if change will trigger here
17846
- // but might as well be safe
17847
- return;
17848
- }
17849
- if (this.props.onChange) {
17850
- this.props.onChange(widgetType);
17851
- }
17852
- };
17853
- }
17854
- shouldComponentUpdate() {
17855
- return false;
17856
- }
17857
- render() {
17858
- const widgets = Widgets.getPublicWidgets();
17859
- const orderedWidgetNames = _.sortBy(_.keys(widgets), name => {
17860
- return widgets[name].displayName;
17861
- });
17862
- const addWidgetString = "Add a widget\u2026";
17863
- return /*#__PURE__*/React.createElement("select", {
17864
- value: "",
17865
- onChange: this.handleChange
17866
- }, /*#__PURE__*/React.createElement("option", {
17867
- value: ""
17868
- }, addWidgetString), /*#__PURE__*/React.createElement("option", {
17869
- disabled: true
17870
- }, "--"), _.map(orderedWidgetNames, name => {
17871
- return /*#__PURE__*/React.createElement("option", {
17872
- key: name,
17873
- value: name
17874
- }, widgets[name].displayName);
17875
- }));
17876
- }
17877
- }
17878
17735
  const _upgradeWidgetInfo = props => {
17879
17736
  // We can't call serialize here because this.refs.widget
17880
17737
  // doesn't exist before this component is mounted.
@@ -18009,6 +17866,153 @@ class WidgetEditor extends React.Component {
18009
17866
  }
18010
17867
  }
18011
17868
 
17869
+ class WidgetSelect extends React.Component {
17870
+ constructor(...args) {
17871
+ super(...args);
17872
+ this.handleChange = e => {
17873
+ const widgetType = e.currentTarget.value;
17874
+ if (widgetType === "") {
17875
+ // TODO(alpert): Not sure if change will trigger here
17876
+ // but might as well be safe
17877
+ return;
17878
+ }
17879
+ if (this.props.onChange) {
17880
+ this.props.onChange(widgetType);
17881
+ }
17882
+ };
17883
+ }
17884
+ shouldComponentUpdate() {
17885
+ return false;
17886
+ }
17887
+ render() {
17888
+ const widgets = Widgets.getPublicWidgets();
17889
+ const orderedWidgetNames = _.sortBy(_.keys(widgets), name => {
17890
+ return widgets[name].displayName;
17891
+ });
17892
+ const addWidgetString = "Add a widget\u2026";
17893
+ return /*#__PURE__*/React.createElement("select", {
17894
+ value: "",
17895
+ onChange: this.handleChange
17896
+ }, /*#__PURE__*/React.createElement("option", {
17897
+ value: ""
17898
+ }, addWidgetString), /*#__PURE__*/React.createElement("option", {
17899
+ disabled: true
17900
+ }, "--"), _.map(orderedWidgetNames, name => {
17901
+ return /*#__PURE__*/React.createElement("option", {
17902
+ key: name,
17903
+ value: name
17904
+ }, widgets[name].displayName);
17905
+ }));
17906
+ }
17907
+ }
17908
+
17909
+ /**
17910
+ * Displays a collapsable list of KaTeX rendering errors.
17911
+ */
17912
+ class KatexErrorView extends React.Component {
17913
+ constructor(...args) {
17914
+ super(...args);
17915
+ this.state = {
17916
+ showErrors: false
17917
+ };
17918
+ this.handleToggleKatexErrors = e => {
17919
+ this.setState({
17920
+ showErrors: !this.state.showErrors
17921
+ });
17922
+ };
17923
+ }
17924
+ render() {
17925
+ const {
17926
+ errorList
17927
+ } = this.props;
17928
+ const {
17929
+ showErrors
17930
+ } = this.state;
17931
+
17932
+ // TODO(riley) replace with SVG icons
17933
+ const disclosureClass = showErrors ? "icon-chevron-down" : "icon-chevron-right";
17934
+ return /*#__PURE__*/React.createElement("div", {
17935
+ className: css(styles$e.errorContainer)
17936
+ }, /*#__PURE__*/React.createElement("div", {
17937
+ className: css(styles$e.title),
17938
+ onClick: this.handleToggleKatexErrors
17939
+ }, /*#__PURE__*/React.createElement("i", {
17940
+ className: disclosureClass,
17941
+ style: {
17942
+ fontSize: 14
17943
+ }
17944
+ }), "\xA0 KaTeX Errors (", errorList.length, ")"), showErrors && /*#__PURE__*/React.createElement("div", {
17945
+ className: css(styles$e.errorExplanation)
17946
+ }, "These errors will cause your LaTeX to load really slowly for the student. Please fix them if you can. If you can\u2019t because KaTeX doesn\u2019t support the feature you need, please message Cam."), showErrors && errorList.map((e, index) => /*#__PURE__*/React.createElement("div", {
17947
+ className: css(styles$e.error),
17948
+ key: index
17949
+ }, /*#__PURE__*/React.createElement("div", {
17950
+ style: {
17951
+ color: "red"
17952
+ }
17953
+ }, e.math), /*#__PURE__*/React.createElement("div", null, e.message))));
17954
+ }
17955
+ }
17956
+ KatexErrorView.propTypes = {
17957
+ errorList: PropTypes.arrayOf(PropTypes.shape({
17958
+ math: PropTypes.string.isRequired,
17959
+ message: PropTypes.string.isRequired
17960
+ })).isRequired
17961
+ };
17962
+ const styles$e = StyleSheet.create({
17963
+ title: {
17964
+ backgroundColor: "#eee",
17965
+ fontSize: "1.25em",
17966
+ padding: "4px 10px"
17967
+ },
17968
+ errorContainer: {
17969
+ border: "1px solid #ddd",
17970
+ borderTop: "none"
17971
+ },
17972
+ errorExplanation: {
17973
+ padding: "4px 10px",
17974
+ backgroundColor: "pink"
17975
+ },
17976
+ error: {
17977
+ padding: "4px 10px"
17978
+ }
17979
+ });
17980
+
17981
+ // like [[snowman input-number 1]]
17982
+ const widgetPlaceholder = "[[\u2603 {id}]]";
17983
+ const widgetRegExp = "(\\[\\[\u2603 {id}\\]\\])";
17984
+ const rWidgetSplit = new RegExp(widgetRegExp.replace("{id}", "[a-z-]+ [0-9]+"), "g");
17985
+ const shortcutRegexp = /^\[\[([a-z-]+)$/; // like [[nu, [[int, etc
17986
+
17987
+ const ENDS_WITH_A_PARAGRAPH = /(?:\n{2,}|^\n*)$/;
17988
+ const TRAILING_NEWLINES = /(\n*)$/;
17989
+ const LEADING_NEWLINES = /^(\n*)/;
17990
+ const commafyInteger = n => {
17991
+ let str = n.toString();
17992
+ if (str.length >= 5) {
17993
+ str = str.replace(/(\d)(?=(\d{3})+$)/g, "$1{,}");
17994
+ }
17995
+ return str;
17996
+ };
17997
+ const makeEndWithAParagraphIfNecessary = content => {
17998
+ if (!ENDS_WITH_A_PARAGRAPH.test(content)) {
17999
+ const match = TRAILING_NEWLINES.exec(content);
18000
+ if (match) {
18001
+ const newlines = match[1];
18002
+ return content + "\n\n".slice(0, 2 - newlines.length);
18003
+ }
18004
+ }
18005
+ return content;
18006
+ };
18007
+ const makeStartWithAParagraphAlways = content => {
18008
+ const match = LEADING_NEWLINES.exec(content);
18009
+ if (!match) {
18010
+ return content;
18011
+ }
18012
+ const newlines = match[1];
18013
+ return "\n\n".slice(0, 2 - newlines.length) + content;
18014
+ };
18015
+
18012
18016
  // This is more general than the actual markdown image parsing regex,
18013
18017
  // which is fine for correctness since it should only mean we could
18014
18018
  // store extra image dimensions, unless the question is insanely
@@ -18708,11 +18712,7 @@ class Editor extends React.Component {
18708
18712
  // }, this);
18709
18713
 
18710
18714
  this.widgetIds = _.keys(widgets);
18711
- widgetsDropDown =
18712
- /*#__PURE__*/
18713
- // eslint-disable-next-line react/no-string-refs
18714
- React.createElement(WidgetSelect, {
18715
- ref: "widgetSelect",
18715
+ widgetsDropDown = /*#__PURE__*/React.createElement(WidgetSelect, {
18716
18716
  onChange: this._addWidget
18717
18717
  });
18718
18718
  const insertTemplateString = "Insert template\u2026";
@@ -26898,6 +26898,80 @@ InteractionEditor.propTypes = _extends({}, Changeable.propTypes, {
26898
26898
  InteractionEditor.widgetName = "interaction";
26899
26899
  InteractionEditor.defaultProps = defaultInteractionProps;
26900
26900
 
26901
+ const UNLIMITED = "unlimited";
26902
+ // Parses a string (ostensibly from some UI element that only supports string
26903
+ // types, such as a <select />) into a numeric point count or the special sigil
26904
+ // "unlimited". Note that 0 is a special case, and means "unlimited".
26905
+ const parsePointCount = points => {
26906
+ const parsed = parseInt(points, 10);
26907
+ if (isNaN(parsed)) {
26908
+ return UNLIMITED;
26909
+ }
26910
+ return parsed === 0 ? UNLIMITED : parsed;
26911
+ };
26912
+
26913
+ const GraphPointsCountSelector = ({
26914
+ numPoints: _numPoints = 1,
26915
+ onChange
26916
+ }) => {
26917
+ return /*#__PURE__*/React.createElement("select", {
26918
+ value: _numPoints,
26919
+ onChange: e => {
26920
+ onChange(parsePointCount(e.target.value));
26921
+ }
26922
+ }, [...Array(7).keys()].map(n => /*#__PURE__*/React.createElement("option", {
26923
+ key: n,
26924
+ value: n
26925
+ }, `${n} point${n > 1 ? "s" : ""}`)), /*#__PURE__*/React.createElement("option", {
26926
+ value: UNLIMITED
26927
+ }, "unlimited"));
26928
+ };
26929
+
26930
+ const GraphTypeSelector = props => {
26931
+ return /*#__PURE__*/React.createElement("select", {
26932
+ value: props.graphType,
26933
+ onChange: e => {
26934
+ const type = e.target.value;
26935
+ props.onChange(type);
26936
+ }
26937
+ }, /*#__PURE__*/React.createElement("option", {
26938
+ value: "linear"
26939
+ }, "Linear function"), /*#__PURE__*/React.createElement("option", {
26940
+ value: "quadratic"
26941
+ }, "Quadratic function"), /*#__PURE__*/React.createElement("option", {
26942
+ value: "sinusoid"
26943
+ }, "Sinusoid function"), /*#__PURE__*/React.createElement("option", {
26944
+ value: "circle"
26945
+ }, "Circle"), /*#__PURE__*/React.createElement("option", {
26946
+ value: "point"
26947
+ }, "Point(s)"), /*#__PURE__*/React.createElement("option", {
26948
+ value: "linear-system"
26949
+ }, "Linear System"), /*#__PURE__*/React.createElement("option", {
26950
+ value: "polygon"
26951
+ }, "Polygon"), /*#__PURE__*/React.createElement("option", {
26952
+ value: "segment"
26953
+ }, "Line Segment(s)"), /*#__PURE__*/React.createElement("option", {
26954
+ value: "ray"
26955
+ }, "Ray"), /*#__PURE__*/React.createElement("option", {
26956
+ value: "angle"
26957
+ }, "Angle"));
26958
+ };
26959
+
26960
+ const SegmentCountSelector = ({
26961
+ numSegments: _numSegments = 1,
26962
+ onChange
26963
+ }) => /*#__PURE__*/React.createElement("select", {
26964
+ key: "segment-select",
26965
+ value: _numSegments,
26966
+ onChange: e => {
26967
+ const num = +e.target.value;
26968
+ onChange(num);
26969
+ }
26970
+ }, _.range(1, 7).map(n => /*#__PURE__*/React.createElement("option", {
26971
+ key: n,
26972
+ value: n
26973
+ }, `${n} segment${n > 1 ? "s" : ""}`)));
26974
+
26901
26975
  const {
26902
26976
  InfoTip: InfoTip$e
26903
26977
  } = components;
@@ -26917,38 +26991,41 @@ const deprecatedProps = {
26917
26991
  };
26918
26992
  }
26919
26993
  };
26920
- const InteractiveGraphEditor = createReactClass({
26921
- displayName: "InteractiveGraphEditor",
26922
- className: "perseus-widget-interactive-graph",
26923
- statics: {
26924
- widgetName: "interactive-graph"
26925
- },
26926
- getDefaultProps: function () {
26927
- return {
26928
- labels: ["x", "y"],
26929
- range: [[-10, 10], [-10, 10]],
26930
- step: [1, 1],
26931
- valid: true,
26932
- backgroundImage: defaultBackgroundImage$1,
26933
- markings: "graph",
26934
- showProtractor: false,
26935
- showRuler: false,
26936
- showTooltips: false,
26937
- rulerLabel: "",
26938
- rulerTicks: 10,
26939
- correct: {
26940
- type: "linear",
26941
- coords: null
26942
- }
26943
- };
26944
- },
26945
- // TODO(jack): Use versioning instead of DeprecationMixin
26946
- deprecatedProps: deprecatedProps,
26994
+ // [min, max]
26995
+
26996
+ // Renders the given children in a View laid out horizontally.
26997
+ const Row = props => {
26998
+ const {
26999
+ children,
27000
+ style
27001
+ } = props;
27002
+ return /*#__PURE__*/React.createElement(View, {
27003
+ style: _extends({}, style, {
27004
+ flexDirection: "row",
27005
+ marginTop: Spacing.xSmall_8
27006
+ })
27007
+ }, children);
27008
+ };
27009
+ const FieldLabel = props => {
27010
+ return /*#__PURE__*/React.createElement(Typography.LabelSmall, {
27011
+ style: {
27012
+ marginRight: Spacing.xSmall_8
27013
+ }
27014
+ }, props.children);
27015
+ };
27016
+ class InteractiveGraphEditor extends React.Component {
27017
+ constructor(...args) {
27018
+ super(...args);
27019
+ this.displayName = "InteractiveGraphEditor";
27020
+ this.className = "perseus-widget-interactive-graph";
27021
+ this.deprecatedProps = deprecatedProps;
27022
+ }
26947
27023
  // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
26948
27024
  UNSAFE_componentWillMount() {
26949
27025
  DeprecationMixin.UNSAFE_componentWillMount.call(this);
26950
- },
26951
- render: function () {
27026
+ }
27027
+ render() {
27028
+ var _this$props$graph$typ, _this$props$graph, _this$props$correct, _this$props$correct2, _this$props$correct3, _this$props$correct4, _this$props$correct5, _this$props$correct6, _this$props$correct7, _this$props$correct8, _this$props$correct9, _this$props$correct10;
26952
27029
  let graph;
26953
27030
  let equationString;
26954
27031
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, interactiveSizes.defaultBoxSize);
@@ -27007,9 +27084,114 @@ const InteractiveGraphEditor = createReactClass({
27007
27084
  className: "perseus-error"
27008
27085
  }, this.props.valid);
27009
27086
  }
27010
- return /*#__PURE__*/React.createElement("div", {
27011
- className: "perseus-widget-interactive-graph"
27012
- }, /*#__PURE__*/React.createElement("div", null, "Correct answer", " ", /*#__PURE__*/React.createElement(InfoTip$e, null, /*#__PURE__*/React.createElement("p", null, "Graph the correct answer in the graph below and ensure the equation or point coordinates displayed represent the correct answer.")), " ", ": ", equationString), /*#__PURE__*/React.createElement(GraphSettings, {
27087
+ return /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Type of Graph:"), /*#__PURE__*/React.createElement(GraphTypeSelector, {
27088
+ graphType: (_this$props$graph$typ = (_this$props$graph = this.props.graph) == null ? void 0 : _this$props$graph.type) != null ? _this$props$graph$typ : InteractiveGraph.defaultProps.graph.type,
27089
+ onChange: type => {
27090
+ this.props.onChange({
27091
+ graph: {
27092
+ type
27093
+ },
27094
+ correct: {
27095
+ type
27096
+ }
27097
+ });
27098
+ }
27099
+ })), ((_this$props$correct = this.props.correct) == null ? void 0 : _this$props$correct.type) === "point" && /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Number of Points:"), /*#__PURE__*/React.createElement(GraphPointsCountSelector, {
27100
+ numPoints: (_this$props$correct2 = this.props.correct) == null ? void 0 : _this$props$correct2.numPoints,
27101
+ onChange: points => {
27102
+ this.props.onChange({
27103
+ correct: {
27104
+ type: "point",
27105
+ numPoints: points
27106
+ }
27107
+ });
27108
+ }
27109
+ })), ((_this$props$correct3 = this.props.correct) == null ? void 0 : _this$props$correct3.type) === "polygon" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Number of sides:"), /*#__PURE__*/React.createElement("select", {
27110
+ key: "polygon-select",
27111
+ value: ((_this$props$correct4 = this.props.correct) == null ? void 0 : _this$props$correct4.numSides) || 3,
27112
+ onChange: e => {
27113
+ const graph = _extends({}, this.props.correct, {
27114
+ numSides: parsePointCount(e.target.value),
27115
+ coords: null,
27116
+ // reset the snap for UNLIMITED, which
27117
+ // only supports "grid"
27118
+ // From: D6578
27119
+ snapTo: "grid"
27120
+ });
27121
+ this.props.onChange({
27122
+ correct: graph
27123
+ });
27124
+ }
27125
+ }, _.map(_.range(3, 13), function (n) {
27126
+ return /*#__PURE__*/React.createElement("option", {
27127
+ key: n,
27128
+ value: n
27129
+ }, `${n} sides`);
27130
+ }), /*#__PURE__*/React.createElement("option", {
27131
+ value: "unlimited"
27132
+ }, "unlimited sides"))), /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Snap to:"), /*#__PURE__*/React.createElement("select", {
27133
+ key: "polygon-snap",
27134
+ value: (_this$props$correct5 = this.props.correct) == null ? void 0 : _this$props$correct5.snapTo,
27135
+ onChange: e => {
27136
+ const graph = _extends({}, this.props.correct, {
27137
+ snapTo: e.target.value,
27138
+ coords: null
27139
+ });
27140
+ this.props.onChange({
27141
+ correct: graph
27142
+ });
27143
+ }
27144
+ }, /*#__PURE__*/React.createElement("option", {
27145
+ value: "grid"
27146
+ }, "grid"), ((_this$props$correct6 = this.props.correct) == null ? void 0 : _this$props$correct6.numSides) !== "unlimited" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("option", {
27147
+ value: "angles"
27148
+ }, "interior angles"), /*#__PURE__*/React.createElement("option", {
27149
+ value: "sides"
27150
+ }, "side measures"))), /*#__PURE__*/React.createElement(InfoTip$e, null, /*#__PURE__*/React.createElement("p", null, "These options affect the movement of the vertex points. The grid option will guide the points to the nearest half step along the grid."), /*#__PURE__*/React.createElement("p", null, "The interior angle and side measure options guide the points to the nearest whole angle or side"), " ", "measure respectively.")), /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Typography.LabelSmall, {
27151
+ tag: "label"
27152
+ }, "Show angle measures:", " ", /*#__PURE__*/React.createElement("input", {
27153
+ type: "checkbox",
27154
+ checked: (_this$props$correct7 = this.props.correct) == null ? void 0 : _this$props$correct7.showAngles,
27155
+ onChange: () => {
27156
+ this.props.onChange({
27157
+ correct: _extends({}, this.props.correct, {
27158
+ showAngles: !this.props.correct.showAngles
27159
+ })
27160
+ });
27161
+ }
27162
+ })), /*#__PURE__*/React.createElement(InfoTip$e, null, /*#__PURE__*/React.createElement("p", null, "Displays the interior angle measures."))), /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Typography.LabelSmall, {
27163
+ tag: "label"
27164
+ }, "Show side measures:", " ", /*#__PURE__*/React.createElement("input", {
27165
+ type: "checkbox",
27166
+ checked: (_this$props$correct8 = this.props.correct) == null ? void 0 : _this$props$correct8.showSides,
27167
+ onChange: () => {
27168
+ this.props.onChange({
27169
+ correct: _extends({}, this.props.correct, {
27170
+ showSides: !this.props.correct.showSides
27171
+ })
27172
+ });
27173
+ }
27174
+ })), /*#__PURE__*/React.createElement(InfoTip$e, null, /*#__PURE__*/React.createElement("p", null, "Displays the side lengths.")))), ((_this$props$correct9 = this.props.correct) == null ? void 0 : _this$props$correct9.type) === "segment" && /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Number of segments: "), /*#__PURE__*/React.createElement(SegmentCountSelector, {
27175
+ numSegments: (_this$props$correct10 = this.props.correct) == null ? void 0 : _this$props$correct10.numSegments,
27176
+ onChange: sides => {
27177
+ this.props.onChange({
27178
+ correct: {
27179
+ type: "segment",
27180
+ numSegments: sides,
27181
+ coords: null
27182
+ }
27183
+ });
27184
+ }
27185
+ })), /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(FieldLabel, null, "Correct answer:"), /*#__PURE__*/React.createElement(Typography.BodyMonospace, {
27186
+ style: {
27187
+ fontSize: 12,
27188
+ backgroundColor: "#eee",
27189
+ paddingInline: Spacing.xxSmall_6,
27190
+ borderColor: "#ccc",
27191
+ borderStyle: "solid",
27192
+ borderWidth: 1
27193
+ }
27194
+ }, equationString), /*#__PURE__*/React.createElement(InfoTip$e, null, /*#__PURE__*/React.createElement("p", null, "Graph the correct answer in the graph below and ensure the equation or point coordinates displayed represent the correct answer."))), /*#__PURE__*/React.createElement(GraphSettings, {
27013
27195
  box: getInteractiveBoxFromSizeClass(sizeClass),
27014
27196
  range: this.props.range,
27015
27197
  labels: this.props.labels,
@@ -27048,21 +27230,22 @@ const InteractiveGraphEditor = createReactClass({
27048
27230
  }, "match exactly"), /*#__PURE__*/React.createElement("option", {
27049
27231
  value: "congruent"
27050
27232
  }, "be congruent"))), /*#__PURE__*/React.createElement(InfoTip$e, null, /*#__PURE__*/React.createElement("p", null, "Congruency requires only that the angle measures are the same. An exact match implies congruency, but also requires that the angles have the same orientation and that the vertices are in the same position.")))), graph);
27051
- },
27052
- changeMatchType: function (e) {
27233
+ }
27234
+ changeMatchType(e) {
27053
27235
  const correct = _.extend({}, this.props.correct, {
27054
27236
  match: e.target.value
27055
27237
  });
27056
27238
  this.props.onChange({
27057
27239
  correct: correct
27058
27240
  });
27059
- },
27060
- serialize: function () {
27241
+ }
27242
+ serialize() {
27061
27243
  const json = _.pick(this.props, "step", "backgroundImage", "markings", "labels", "showProtractor", "showRuler", "showTooltips", "rulerLabel", "rulerTicks", "range", "gridStep", "snapStep");
27062
27244
 
27063
27245
  // eslint-disable-next-line react/no-string-refs
27064
27246
  const graph = this.refs.graph;
27065
27247
  if (graph) {
27248
+ // @ts-expect-error TS2339 Property 'getUserInput' does not exist on type 'ReactInstance'. Property 'getUserInput' does not exist on type 'Component<any, {}, any>'.
27066
27249
  const correct = graph && graph.getUserInput();
27067
27250
  _.extend(json, {
27068
27251
  graph: {
@@ -27077,8 +27260,19 @@ const InteractiveGraphEditor = createReactClass({
27077
27260
  }
27078
27261
  });
27079
27262
  }
27263
+ // @ts-expect-error TS2739 Type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "step" | "gridStep" | "snapStep" | "backgroundImage" | "markings" | "labels" | ... 5 more ... | "range">' is missing the following properties from type 'PerseusInteractiveGraphWidgetOptions': graph, correct
27080
27264
  return json;
27081
27265
  }
27266
+ }
27267
+ InteractiveGraphEditor.widgetName = "interactive-graph";
27268
+ InteractiveGraphEditor.defaultProps = _extends({}, InteractiveGraph.defaultProps, {
27269
+ valid: true,
27270
+ backgroundImage: defaultBackgroundImage$1,
27271
+ showTooltips: false,
27272
+ correct: {
27273
+ type: InteractiveGraph.defaultProps.graph.type,
27274
+ coords: null
27275
+ }
27082
27276
  });
27083
27277
 
27084
27278
  const _excluded$1 = ["forwardedRef", "width", "grow", "shrink", "icon", "backgroundColor", "focusBorderColor", "borderColor", "color", "onSubmit", "leftSideIcon", "id", "testId", "type", "labelMediumInputText"];