@khanacademy/perseus-editor 4.3.0 → 4.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -25,7 +25,7 @@ import * as i18n$1 from '@khanacademy/wonder-blocks-i18n';
25
25
 
26
26
  // This file is processed by a Rollup plugin (replace) to inject the production
27
27
  const libName = "@khanacademy/perseus-editor";
28
- const libVersion = "4.3.0";
28
+ const libVersion = "4.3.2";
29
29
  addLibraryVersionToPerseusDebug(libName, libVersion);
30
30
 
31
31
  function _extends() {
@@ -23768,8 +23768,9 @@ const {
23768
23768
  InfoTip: InfoTip$m
23769
23769
  } = components;
23770
23770
  class ItemExtrasEditor extends React.Component {
23771
- constructor(...args) {
23772
- super(...args);
23771
+ constructor(props) {
23772
+ super(props);
23773
+ this.finCalcOptions = [];
23773
23774
  this.state = {
23774
23775
  financialCalculatorOptionsExpanded: false
23775
23776
  };
@@ -23780,11 +23781,15 @@ class ItemExtrasEditor extends React.Component {
23780
23781
  }
23781
23782
  return data;
23782
23783
  };
23784
+ this.finCalcOptions = [props.financialCalculatorMonthlyPayment, props.financialCalculatorTotalAmount, props.financialCalculatorTimeToPayOff];
23785
+ this.state = {
23786
+ financialCalculatorOptionsExpanded: this.finCalcOptions.some(opt => opt === true)
23787
+ };
23783
23788
  }
23784
- componentDidUpdate(prevProps) {
23789
+ componentDidUpdate() {
23785
23790
  // If no financial calculator options are checked, uncheck the
23786
23791
  // financial calculator option.
23787
- if (this.state.financialCalculatorOptionsExpanded && !this.props.financialCalculatorMonthlyPayment && !this.props.financialCalculatorTotalAmount && !this.props.financialCalculatorTimeToPayOff) {
23792
+ if (this.state.financialCalculatorOptionsExpanded && !this.finCalcOptions.some(option => option === true)) {
23788
23793
  this.setState({
23789
23794
  financialCalculatorOptionsExpanded: false
23790
23795
  });
@@ -23813,7 +23818,7 @@ class ItemExtrasEditor extends React.Component {
23813
23818
  financialCalculatorOptionsExpanded: e.target.checked
23814
23819
  });
23815
23820
  // If the financial calculator is unchecked,
23816
- // this needs to be reset. All checked by
23821
+ // these need to be reset. All checked by
23817
23822
  // default.
23818
23823
  this.props.onChange({
23819
23824
  financialCalculatorMonthlyPayment: e.target.checked,
@@ -29519,6 +29524,7 @@ const GraphTypeSelector = props => {
29519
29524
  }, "Angle"));
29520
29525
  };
29521
29526
 
29527
+ /* eslint-disable react/forbid-prop-types, react/no-unsafe */
29522
29528
  const {
29523
29529
  ButtonGroup: ButtonGroup$2,
29524
29530
  InfoTip: InfoTip$e,
@@ -29533,64 +29539,8 @@ const defaultBackgroundImage$1 = {
29533
29539
  function numSteps(range, step) {
29534
29540
  return Math.floor((range[1] - range[0]) / step);
29535
29541
  }
29536
- const InteractiveGraphSettings = createReactClass({
29537
- displayName: "InteractiveGraphSettings",
29538
- propTypes: _extends({}, Changeable.propTypes, {
29539
- editableSettings: PropTypes.arrayOf(PropTypes.oneOf(["canvas", "graph", "snap", "image", "measure"])),
29540
- box: PropTypes.arrayOf(PropTypes.number),
29541
- labels: PropTypes.arrayOf(PropTypes.string),
29542
- range: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)),
29543
- step: PropTypes.arrayOf(PropTypes.number),
29544
- gridStep: PropTypes.arrayOf(PropTypes.number),
29545
- snapStep: PropTypes.arrayOf(PropTypes.number),
29546
- valid: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
29547
- backgroundImage: PropTypes.object,
29548
- markings: PropTypes.oneOf(["graph", "grid", "none"]),
29549
- showProtractor: PropTypes.bool,
29550
- showRuler: PropTypes.bool,
29551
- showTooltips: PropTypes.bool,
29552
- rulerLabel: PropTypes.string,
29553
- rulerTicks: PropTypes.number
29554
- }),
29555
- getDefaultProps: function () {
29556
- return {
29557
- editableSettings: ["graph", "snap", "image", "measure"],
29558
- box: [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall],
29559
- labels: ["x", "y"],
29560
- range: [[-10, 10], [-10, 10]],
29561
- step: [1, 1],
29562
- gridStep: [1, 1],
29563
- snapStep: [1, 1],
29564
- valid: true,
29565
- backgroundImage: defaultBackgroundImage$1,
29566
- markings: "graph",
29567
- showProtractor: false,
29568
- showRuler: false,
29569
- showTooltips: false,
29570
- rulerLabel: "",
29571
- rulerTicks: 10
29572
- };
29573
- },
29574
- getInitialState: function () {
29575
- return this.stateFromProps(this.props);
29576
- },
29577
- componentDidMount: function () {
29578
- // TODO(scottgrant): This is a hack to remove the deprecated call to
29579
- // this.isMounted() but is still considered an anti-pattern.
29580
- this._isMounted = true;
29581
- this.changeGraph = _.debounce(this.changeGraph, 300);
29582
- },
29583
- UNSAFE_componentWillReceiveProps: function (nextProps) {
29584
- // Make sure that state updates when switching
29585
- // between different items in a multi-item editor.
29586
- if (!_.isEqual(this.props.labels, nextProps.labels) || !_.isEqual(this.props.gridStep, nextProps.gridStep) || !_.isEqual(this.props.snapStep, nextProps.snapStep) || !_.isEqual(this.props.step, nextProps.step) || !_.isEqual(this.props.range, nextProps.range) || !_.isEqual(this.props.backgroundImage, nextProps.backgroundImage)) {
29587
- this.setState(this.stateFromProps(nextProps));
29588
- }
29589
- },
29590
- componentWillUnmount: function () {
29591
- this._isMounted = false;
29592
- },
29593
- stateFromProps: function (props) {
29542
+ class InteractiveGraphSettings extends React.Component {
29543
+ static stateFromProps(props) {
29594
29544
  return {
29595
29545
  labelsTextbox: props.labels,
29596
29546
  gridStepTextbox: props.gridStep,
@@ -29599,244 +29549,267 @@ const InteractiveGraphSettings = createReactClass({
29599
29549
  rangeTextbox: props.range,
29600
29550
  backgroundImage: _.clone(props.backgroundImage)
29601
29551
  };
29602
- },
29603
- change(...args) {
29604
- return Changeable.change.apply(this, args);
29605
- },
29606
- // TODO(aria): Make either a wrapper for standard events to work
29607
- // with this.change, or make these use some TextInput/NumberInput box
29608
- changeRulerLabel: function (e) {
29609
- this.change({
29610
- rulerLabel: e.target.value
29611
- });
29612
- },
29613
- changeRulerTicks: function (e) {
29614
- this.change({
29615
- rulerTicks: +e.target.value
29616
- });
29617
- },
29618
- changeBackgroundUrl: function (e) {
29619
- // Only continue on blur or "enter"
29620
- if (e.type === "keypress" && e.key !== "Enter") {
29621
- return;
29622
- }
29623
- const setUrl = (url, width, height) => {
29624
- const image = _.clone(this.props.backgroundImage);
29625
- image.url = url;
29626
- image.width = width;
29627
- image.height = height;
29628
- this.setState({
29629
- backgroundImage: image
29630
- }, this.changeGraph);
29552
+ }
29553
+ constructor(props) {
29554
+ super(props);
29555
+ this._isMounted = false;
29556
+ this.change = (...args) => {
29557
+ return Changeable.change.apply(this, args);
29558
+ };
29559
+ // TODO(aria): Make either a wrapper for standard events to work
29560
+ // with this.change, or make these use some TextInput/NumberInput box
29561
+ this.changeRulerLabel = e => {
29562
+ this.change({
29563
+ rulerLabel: e.target.value
29564
+ });
29631
29565
  };
29566
+ this.changeRulerTicks = e => {
29567
+ this.change({
29568
+ rulerTicks: +e.target.value
29569
+ });
29570
+ };
29571
+ this.changeBackgroundUrl = e => {
29572
+ // Only continue on blur or "enter"
29573
+ if (e.type === "keypress" && e.key !== "Enter") {
29574
+ return;
29575
+ }
29576
+ const setUrl = (url, width, height) => {
29577
+ const image = _.clone(this.props.backgroundImage);
29578
+ image.url = url;
29579
+ image.width = width;
29580
+ image.height = height;
29581
+ this.setState({
29582
+ backgroundImage: image
29583
+ }, this.changeGraph);
29584
+ };
29632
29585
 
29633
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
29634
- const url = ReactDOM.findDOMNode(this.refs["bg-url"]).value; // eslint-disable-line react/no-string-refs
29635
- if (url) {
29636
- Util.getImageSize(url, (width, height) => {
29637
- if (this._isMounted) {
29638
- setUrl(url, width, height);
29639
- }
29586
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
29587
+ const url = ReactDOM.findDOMNode(this.refs["bg-url"]).value; // eslint-disable-line react/no-string-refs
29588
+ if (url) {
29589
+ Util.getImageSize(url, (width, height) => {
29590
+ if (this._isMounted) {
29591
+ setUrl(url, width, height);
29592
+ }
29593
+ });
29594
+ } else {
29595
+ setUrl(null, 0, 0);
29596
+ }
29597
+ };
29598
+ this.renderLabelChoices = choices => {
29599
+ return _.map(choices, function (nameAndValue) {
29600
+ return /*#__PURE__*/React.createElement("option", {
29601
+ value: nameAndValue[1]
29602
+ }, nameAndValue[0]);
29640
29603
  });
29641
- } else {
29642
- setUrl(null, 0, 0);
29643
- }
29644
- },
29645
- renderLabelChoices: function (choices) {
29646
- return _.map(choices, function (nameAndValue) {
29647
- return /*#__PURE__*/React.createElement("option", {
29648
- value: nameAndValue[1]
29649
- }, nameAndValue[0]);
29650
- });
29651
- },
29652
- validRange: function (range) {
29653
- const numbers = _.every(range, function (num) {
29654
- return _.isFinite(num);
29655
- });
29656
- if (!numbers) {
29657
- return "Range must be a valid number";
29658
- }
29659
- if (range[0] >= range[1]) {
29660
- return "Range must have a higher number on the right";
29661
- }
29662
- return true;
29663
- },
29664
- validateStepValue: function (settings) {
29665
- const {
29666
- step,
29667
- range,
29668
- name,
29669
- minTicks,
29670
- maxTicks
29671
- } = settings;
29672
- if (!_.isFinite(step)) {
29673
- return name + " must be a valid number";
29674
- }
29675
- const nSteps = numSteps(range, step);
29676
- if (nSteps < minTicks) {
29677
- return name + " is too large, there must be at least " + minTicks + " ticks.";
29678
- }
29679
- if (nSteps > maxTicks) {
29680
- return name + " is too small, there can be at most " + maxTicks + " ticks.";
29681
- }
29682
- return true;
29683
- },
29684
- validSnapStep: function (step, range) {
29685
- return this.validateStepValue({
29686
- step: step,
29687
- range: range,
29688
- name: "Snap step",
29689
- minTicks: 5,
29690
- maxTicks: 60
29691
- });
29692
- },
29693
- validGridStep: function (step, range) {
29694
- return this.validateStepValue({
29695
- step: step,
29696
- range: range,
29697
- name: "Grid step",
29698
- minTicks: 3,
29699
- maxTicks: 60
29700
- });
29701
- },
29702
- validStep: function (step, range) {
29703
- return this.validateStepValue({
29704
- step: step,
29705
- range: range,
29706
- name: "Step",
29707
- minTicks: 3,
29708
- maxTicks: 20
29709
- });
29710
- },
29711
- validBackgroundImageSize: function (image) {
29712
- // Ignore empty images
29713
- if (!image.url) {
29604
+ };
29605
+ this.validRange = range => {
29606
+ const numbers = _.every(range, function (num) {
29607
+ return _.isFinite(num);
29608
+ });
29609
+ if (!numbers) {
29610
+ return "Range must be a valid number";
29611
+ }
29612
+ if (range[0] >= range[1]) {
29613
+ return "Range must have a higher number on the right";
29614
+ }
29714
29615
  return true;
29715
- }
29716
- const validSize = image.width <= 450 && image.height <= 450;
29717
- if (!validSize) {
29718
- return "Image must be smaller than 450px x 450px.";
29719
- }
29720
- return true;
29721
- },
29722
- validateGraphSettings: function (range, step, gridStep, snapStep, image) {
29723
- const self = this;
29724
- let msg;
29725
- const goodRange = _.every(range, function (range) {
29726
- msg = self.validRange(range);
29727
- return msg === true;
29728
- });
29729
- if (!goodRange) {
29730
- return msg;
29731
- }
29732
- const goodStep = _.every(step, function (step, i) {
29733
- msg = self.validStep(step, range[i]);
29734
- return msg === true;
29735
- });
29736
- if (!goodStep) {
29737
- return msg;
29738
- }
29739
- const goodGridStep = _.every(gridStep, function (gridStep, i) {
29740
- msg = self.validGridStep(gridStep, range[i]);
29741
- return msg === true;
29742
- });
29743
- if (!goodGridStep) {
29744
- return msg;
29745
- }
29746
- const goodSnapStep = _.every(snapStep, function (snapStep, i) {
29747
- msg = self.validSnapStep(snapStep, range[i]);
29748
- return msg === true;
29749
- });
29750
- if (!goodSnapStep) {
29751
- return msg;
29752
- }
29753
- const goodImageSize = this.validBackgroundImageSize(image);
29754
- if (goodImageSize !== true) {
29755
- msg = goodImageSize;
29756
- return msg;
29757
- }
29758
- return true;
29759
- },
29760
- changeLabel: function (i, e) {
29761
- const val = e.target.value;
29762
- const labels = this.state.labelsTextbox.slice();
29763
- labels[i] = val;
29764
- this.setState({
29765
- labelsTextbox: labels
29766
- }, this.changeGraph);
29767
- },
29768
- changeRange: function (i, values) {
29769
- const ranges = this.state.rangeTextbox.slice();
29770
- ranges[i] = values;
29771
- const step = this.state.stepTextbox.slice();
29772
- const gridStep = this.state.gridStepTextbox.slice();
29773
- const snapStep = this.state.snapStepTextbox.slice();
29774
- const scale = Util.scaleFromExtent(ranges[i], this.props.box[i]);
29775
- if (this.validRange(ranges[i]) === true) {
29776
- step[i] = Util.tickStepFromExtent(ranges[i], this.props.box[i]);
29777
- gridStep[i] = Util.gridStepFromTickStep(step[i], scale);
29778
- snapStep[i] = gridStep[i] / 2;
29779
- }
29780
- this.setState({
29781
- stepTextbox: step,
29782
- gridStepTextbox: gridStep,
29783
- snapStepTextbox: snapStep,
29784
- rangeTextbox: ranges
29785
- }, this.changeGraph);
29786
- },
29787
- changeStep: function (step) {
29788
- this.setState({
29789
- stepTextbox: step
29790
- }, this.changeGraph);
29791
- },
29792
- changeSnapStep: function (snapStep) {
29793
- this.setState({
29794
- snapStepTextbox: snapStep
29795
- }, this.changeGraph);
29796
- },
29797
- changeGridStep: function (gridStep) {
29798
- this.setState({
29799
- gridStepTextbox: gridStep,
29800
- snapStepTextbox: _.map(gridStep, function (step) {
29801
- return step / 2;
29802
- })
29803
- }, this.changeGraph);
29804
- },
29805
- changeGraph: function () {
29806
- const labels = this.state.labelsTextbox;
29807
- const range = _.map(this.state.rangeTextbox, function (range) {
29808
- return _.map(range, Number);
29809
- });
29810
- const step = _.map(this.state.stepTextbox, Number);
29811
- const gridStep = this.state.gridStepTextbox;
29812
- const snapStep = this.state.snapStepTextbox;
29813
- const image = this.state.backgroundImage;
29814
-
29815
- // validationResult is either:
29816
- // true -> the settings are valid
29817
- // a string -> the settings are invalid, and the explanation
29818
- // is contained in the string
29819
- // TODO(aria): Refactor this to not be confusing
29820
- const validationResult = this.validateGraphSettings(range, step, gridStep, snapStep, image);
29821
- if (validationResult === true) {
29822
- // either true or a string
29823
- this.change({
29824
- valid: true,
29825
- labels: labels,
29616
+ };
29617
+ this.validateStepValue = settings => {
29618
+ const {
29619
+ step,
29620
+ range,
29621
+ name,
29622
+ minTicks,
29623
+ maxTicks
29624
+ } = settings;
29625
+ if (!_.isFinite(step)) {
29626
+ return name + " must be a valid number";
29627
+ }
29628
+ const nSteps = numSteps(range, step);
29629
+ if (nSteps < minTicks) {
29630
+ return name + " is too large, there must be at least " + minTicks + " ticks.";
29631
+ }
29632
+ if (nSteps > maxTicks) {
29633
+ return name + " is too small, there can be at most " + maxTicks + " ticks.";
29634
+ }
29635
+ return true;
29636
+ };
29637
+ this.validSnapStep = (step, range) => {
29638
+ return this.validateStepValue({
29639
+ step: step,
29826
29640
  range: range,
29641
+ name: "Snap step",
29642
+ minTicks: 5,
29643
+ maxTicks: 60
29644
+ });
29645
+ };
29646
+ this.validGridStep = (step, range) => {
29647
+ return this.validateStepValue({
29827
29648
  step: step,
29828
- gridStep: gridStep,
29829
- snapStep: snapStep,
29830
- backgroundImage: image
29649
+ range: range,
29650
+ name: "Grid step",
29651
+ minTicks: 3,
29652
+ maxTicks: 60
29831
29653
  });
29832
- } else {
29833
- this.change({
29834
- valid: validationResult // a string message, not false
29654
+ };
29655
+ this.validStep = (step, range) => {
29656
+ return this.validateStepValue({
29657
+ step: step,
29658
+ range: range,
29659
+ name: "Step",
29660
+ minTicks: 3,
29661
+ maxTicks: 20
29662
+ });
29663
+ };
29664
+ this.validBackgroundImageSize = image => {
29665
+ // Ignore empty images
29666
+ if (!image.url) {
29667
+ return true;
29668
+ }
29669
+ const validSize = image.width <= 450 && image.height <= 450;
29670
+ if (!validSize) {
29671
+ return "Image must be smaller than 450px x 450px.";
29672
+ }
29673
+ return true;
29674
+ };
29675
+ this.validateGraphSettings = (range, step, gridStep, snapStep, image) => {
29676
+ const self = this;
29677
+ let msg;
29678
+ const goodRange = _.every(range, function (range) {
29679
+ msg = self.validRange(range);
29680
+ return msg === true;
29681
+ });
29682
+ if (!goodRange) {
29683
+ return msg;
29684
+ }
29685
+ const goodStep = _.every(step, function (step, i) {
29686
+ msg = self.validStep(step, range[i]);
29687
+ return msg === true;
29835
29688
  });
29689
+ if (!goodStep) {
29690
+ return msg;
29691
+ }
29692
+ const goodGridStep = _.every(gridStep, function (gridStep, i) {
29693
+ msg = self.validGridStep(gridStep, range[i]);
29694
+ return msg === true;
29695
+ });
29696
+ if (!goodGridStep) {
29697
+ return msg;
29698
+ }
29699
+ const goodSnapStep = _.every(snapStep, function (snapStep, i) {
29700
+ msg = self.validSnapStep(snapStep, range[i]);
29701
+ return msg === true;
29702
+ });
29703
+ if (!goodSnapStep) {
29704
+ return msg;
29705
+ }
29706
+ const goodImageSize = this.validBackgroundImageSize(image);
29707
+ if (goodImageSize !== true) {
29708
+ msg = goodImageSize;
29709
+ return msg;
29710
+ }
29711
+ return true;
29712
+ };
29713
+ this.changeLabel = (i, e) => {
29714
+ const val = e.target.value;
29715
+ const labels = this.state.labelsTextbox.slice();
29716
+ labels[i] = val;
29717
+ this.setState({
29718
+ labelsTextbox: labels
29719
+ }, this.changeGraph);
29720
+ };
29721
+ this.changeRange = (i, values) => {
29722
+ const ranges = this.state.rangeTextbox.slice();
29723
+ ranges[i] = values;
29724
+ const step = this.state.stepTextbox.slice();
29725
+ const gridStep = this.state.gridStepTextbox.slice();
29726
+ const snapStep = this.state.snapStepTextbox.slice();
29727
+ const scale = Util.scaleFromExtent(ranges[i], this.props.box[i]);
29728
+ if (this.validRange(ranges[i]) === true) {
29729
+ step[i] = Util.tickStepFromExtent(ranges[i], this.props.box[i]);
29730
+ const gridStepValue = Util.gridStepFromTickStep(step[i], scale);
29731
+ if (gridStepValue) {
29732
+ gridStep[i] = gridStepValue;
29733
+ }
29734
+ snapStep[i] = gridStep[i] / 2;
29735
+ }
29736
+ this.setState({
29737
+ stepTextbox: step,
29738
+ gridStepTextbox: gridStep,
29739
+ snapStepTextbox: snapStep,
29740
+ rangeTextbox: ranges
29741
+ }, this.changeGraph);
29742
+ };
29743
+ this.changeStep = step => {
29744
+ this.setState({
29745
+ stepTextbox: step
29746
+ }, this.changeGraph);
29747
+ };
29748
+ this.changeSnapStep = snapStep => {
29749
+ this.setState({
29750
+ snapStepTextbox: snapStep
29751
+ }, this.changeGraph);
29752
+ };
29753
+ this.changeGridStep = gridStep => {
29754
+ this.setState({
29755
+ gridStepTextbox: gridStep,
29756
+ snapStepTextbox: _.map(gridStep, function (step) {
29757
+ return step / 2;
29758
+ })
29759
+ }, this.changeGraph);
29760
+ };
29761
+ this.changeGraph = () => {
29762
+ const labels = this.state.labelsTextbox;
29763
+ const range = _.map(this.state.rangeTextbox, function (range) {
29764
+ return _.map(range, Number);
29765
+ });
29766
+ const step = _.map(this.state.stepTextbox, Number);
29767
+ const gridStep = this.state.gridStepTextbox;
29768
+ const snapStep = this.state.snapStepTextbox;
29769
+ const image = this.state.backgroundImage;
29770
+
29771
+ // validationResult is either:
29772
+ // true -> the settings are valid
29773
+ // a string -> the settings are invalid, and the explanation
29774
+ // is contained in the string
29775
+ // TODO(aria): Refactor this to not be confusing
29776
+ const validationResult = this.validateGraphSettings(range, step, gridStep, snapStep, image);
29777
+ if (validationResult === true) {
29778
+ // either true or a string
29779
+ this.change({
29780
+ valid: true,
29781
+ labels: labels,
29782
+ range: range,
29783
+ step: step,
29784
+ gridStep: gridStep,
29785
+ snapStep: snapStep,
29786
+ backgroundImage: image
29787
+ });
29788
+ } else {
29789
+ this.change({
29790
+ valid: validationResult // a string message, not false
29791
+ });
29792
+ }
29793
+ };
29794
+ this.state = InteractiveGraphSettings.stateFromProps(props);
29795
+ }
29796
+ componentDidMount() {
29797
+ // TODO(scottgrant): This is a hack to remove the deprecated call to
29798
+ // this.isMounted() but is still considered an anti-pattern.
29799
+ this._isMounted = true;
29800
+ this.changeGraph = _.debounce(this.changeGraph, 300);
29801
+ }
29802
+ UNSAFE_componentWillReceiveProps(nextProps) {
29803
+ // Make sure that state updates when switching
29804
+ // between different items in a multi-item editor.
29805
+ if (!_.isEqual(this.props.labels, nextProps.labels) || !_.isEqual(this.props.gridStep, nextProps.gridStep) || !_.isEqual(this.props.snapStep, nextProps.snapStep) || !_.isEqual(this.props.step, nextProps.step) || !_.isEqual(this.props.range, nextProps.range) || !_.isEqual(this.props.backgroundImage, nextProps.backgroundImage)) {
29806
+ this.setState(InteractiveGraphSettings.stateFromProps(nextProps));
29836
29807
  }
29837
- },
29838
-
29839
- render: function () {
29808
+ }
29809
+ componentWillUnmount() {
29810
+ this._isMounted = false;
29811
+ }
29812
+ render() {
29840
29813
  const scale = [KhanMath.roundTo(2, Util.scaleFromExtent(this.props.range[0], this.props.box[0])), KhanMath.roundTo(2, Util.scaleFromExtent(this.props.range[1], this.props.box[1]))];
29841
29814
  const {
29842
29815
  TeX
@@ -30010,7 +29983,24 @@ const InteractiveGraphSettings = createReactClass({
30010
29983
  }, n);
30011
29984
  })))))));
30012
29985
  }
30013
- });
29986
+ }
29987
+ InteractiveGraphSettings.defaultProps = {
29988
+ editableSettings: ["graph", "snap", "image", "measure"],
29989
+ box: [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall],
29990
+ labels: ["x", "y"],
29991
+ range: [[-10, 10], [-10, 10]],
29992
+ step: [1, 1],
29993
+ gridStep: [1, 1],
29994
+ snapStep: [1, 1],
29995
+ valid: true,
29996
+ backgroundImage: defaultBackgroundImage$1,
29997
+ markings: "graph",
29998
+ showProtractor: false,
29999
+ showRuler: false,
30000
+ showTooltips: false,
30001
+ rulerLabel: "",
30002
+ rulerTicks: 10
30003
+ };
30014
30004
 
30015
30005
  const SegmentCountSelector = ({
30016
30006
  numSegments: _numSegments = 1,