@khanacademy/perseus 22.0.2 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,6 @@
1
1
  import * as React from "react";
2
2
  type Props = {
3
- children: React.ReactNode;
3
+ children: string | React.ReactElement | React.ReactElement[];
4
4
  };
5
- type State = {
6
- hover: boolean;
7
- };
8
- declare class InfoTip extends React.Component<Props, State> {
9
- state: State;
10
- handleMouseEnter: () => void;
11
- handleMouseLeave: () => void;
12
- render(): React.ReactNode;
13
- }
5
+ export declare const InfoTip: (props: Props) => JSX.Element;
14
6
  export default InfoTip;
package/dist/es/index.css CHANGED
@@ -2470,7 +2470,10 @@ img.zoom-img {
2470
2470
  padding-top: 5px;
2471
2471
  }
2472
2472
  .framework-perseus .svg-image {
2473
- display: inline-block;
2473
+ display: block;
2474
+ margin: 0 auto;
2475
+ margin-inline-start: auto;
2476
+ margin-inline-end: auto;
2474
2477
  }
2475
2478
  .framework-perseus .unresponsive-svg-image,
2476
2479
  .framework-perseus .perseus-rendered-radio .unresponsive-svg-image {
@@ -2750,6 +2753,16 @@ body.mobile .categorizer-container td.category input[type="radio"] + span:active
2750
2753
  .perseus-image-editor .graph-settings {
2751
2754
  margin-top: 5px;
2752
2755
  }
2756
+ .perseus-image-widget {
2757
+ margin-inline-start: auto;
2758
+ margin-inline-end: auto;
2759
+ }
2760
+ .perseus-hint-renderer .perseus-image-widget {
2761
+ margin-inline-start: 0;
2762
+ }
2763
+ .perseus-hint-renderer .svg-image {
2764
+ margin-inline-start: 0;
2765
+ }
2753
2766
  .perseus-widget-interactive-graph {
2754
2767
  box-sizing: content-box;
2755
2768
  padding: 25px 25px 0 0;
package/dist/es/index.js CHANGED
@@ -29,10 +29,10 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
29
29
  import caretDown from '@phosphor-icons/core/assets/regular/caret-down.svg';
30
30
  import caretUp from '@phosphor-icons/core/assets/regular/caret-up.svg';
31
31
  import debounce$2 from 'lodash.debounce';
32
- import { Line as Line$3, Point as Point$2, useTransformContext, vec, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
32
+ import { vec, Line as Line$3, Point as Point$2, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
33
33
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
34
+ import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
34
35
  import questionIcon from '@phosphor-icons/core/regular/question.svg';
35
- import { LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
36
36
  import createReactClass from 'create-react-class';
37
37
  import Switch from '@khanacademy/wonder-blocks-switch';
38
38
  import Pill from '@khanacademy/wonder-blocks-pill';
@@ -28477,6 +28477,89 @@ Graph.defaultProps = {
28477
28477
  isMobile: false
28478
28478
  };
28479
28479
 
28480
+ const defaultGraphConfig = {
28481
+ range: [[0, 1], [0, 1]],
28482
+ snapStep: [1, 1],
28483
+ markings: "none",
28484
+ showTooltips: false,
28485
+ graphDimensionsInPixels: [1, 1],
28486
+ width: 0,
28487
+ height: 0,
28488
+ labels: []
28489
+ };
28490
+ const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
28491
+ function useGraphConfig() {
28492
+ return React__default.useContext(GraphConfigContext);
28493
+ }
28494
+
28495
+ const matrixBuilder = vec.matrixBuilder;
28496
+ // When converting vectors, we don't need to translate because a vector
28497
+ // is like a pair of x and y distances, which are not bound to specific graph locations.
28498
+ function vectorsToPixels(vectors, graphState, translation = [0, 0]) {
28499
+ const {
28500
+ range,
28501
+ width,
28502
+ height
28503
+ } = graphState;
28504
+ const [[xMin, xMax], [yMin, yMax]] = range;
28505
+ const transformToPx = matrixBuilder().translate(...translation).scale(width / (xMax - xMin), -height / (yMax - yMin)).get();
28506
+ return vectors.map(p => vec.transform(p, transformToPx));
28507
+ }
28508
+
28509
+ // When converting points, we translate so the pixel point ends up in the right location.
28510
+ // This is because points are specific locations on graphs, unlike vectors.
28511
+ function pointToPixel(point, graphState) {
28512
+ const [[xMin], [, yMax]] = graphState.range;
28513
+ const [a, b] = [-xMin, -yMax];
28514
+ return vectorsToPixels([point], graphState, [a, b])[0];
28515
+ }
28516
+ const useTransformVectorsToPixels = (...vectors) => {
28517
+ const graphState = useGraphConfig();
28518
+ return vectorsToPixels(vectors, graphState);
28519
+ };
28520
+
28521
+ function AxisLabels() {
28522
+ const {
28523
+ range,
28524
+ labels,
28525
+ width,
28526
+ height
28527
+ } = useGraphConfig();
28528
+ const yAxisLabelLocation = [0, range[1][1]];
28529
+ const xAxisLabelLocation = [range[0][1], 0];
28530
+ const [xAxisLabelText, yAxisLabelText] = labels;
28531
+ const graphInfo = {
28532
+ range,
28533
+ width,
28534
+ height
28535
+ };
28536
+ const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
28537
+ // The default location for the y-axis-label is at the maximum y point
28538
+ // This is just underneath the tick line for that point. The -24 moves the
28539
+ // label up one grid square, so it sits on top of the graph
28540
+ const [x2, y2] = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
28541
+ const {
28542
+ TeX
28543
+ } = getDependencies();
28544
+ return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
28545
+ style: {
28546
+ position: "absolute",
28547
+ left: x1,
28548
+ top: y1,
28549
+ fontSize: "14px",
28550
+ transform: "translate(7px, -50%)"
28551
+ }
28552
+ }, /*#__PURE__*/React__default.createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default.createElement("span", {
28553
+ style: {
28554
+ position: "absolute",
28555
+ left: x2,
28556
+ top: y2,
28557
+ fontSize: "14px",
28558
+ transform: "translate(-50%, 0px)"
28559
+ }
28560
+ }, /*#__PURE__*/React__default.createElement(TeX, null, yAxisLabelText)));
28561
+ }
28562
+
28480
28563
  // TODO(FEI-4010): Remove `Perseus` prefix for all types here
28481
28564
  // TODO(FEI-4011): Use types generated by https://github.com/jaredly/generate-perseus-flowtypes
28482
28565
 
@@ -28768,26 +28851,17 @@ function snap(snapStep, point) {
28768
28851
  return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
28769
28852
  }
28770
28853
 
28771
- const useTransform = (...points) => {
28772
- const {
28773
- viewTransform,
28774
- userTransform
28775
- } = useTransformContext();
28776
- const transformToPx = vec.matrixMult(viewTransform, userTransform);
28777
- const transformedPoints = points.map(point => vec.transform(point, transformToPx));
28778
- return transformedPoints;
28779
- };
28780
-
28781
28854
  /**
28782
- * Given two points, find the tips that extends through the points to the edge of the range.
28783
- * @param collinearPoint - The point that the line passes through. Needed to establish slope.
28784
- * @param extendFrom - The point that the line extends from to the edge of the graph.
28855
+ * Given a ray and a rectangular box, find the point where the ray intersects
28856
+ * the edge of the box. Assumes the `initialPoint` is inside the box.
28857
+ * @param initialPoint - The starting point of the ray.
28858
+ * @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
28859
+ * @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
28785
28860
  */
28786
- const getRayIntersectionCoords = (collinearPoint, extendFrom, range) => {
28787
- // edges of the graph
28788
- const [[xMin, xMax], [yMin, yMax]] = range;
28789
- const [aX, aY] = collinearPoint;
28790
- const [bX, bY] = extendFrom;
28861
+ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
28862
+ const [[xMin, xMax], [yMin, yMax]] = box;
28863
+ const [aX, aY] = throughPoint;
28864
+ const [bX, bY] = initialPoint;
28791
28865
  const yDiff = bY - aY;
28792
28866
  const xDiff = bX - aX;
28793
28867
  const slope = yDiff / xDiff;
@@ -28891,7 +28965,7 @@ function scaleCommandBy(scaleFactor) {
28891
28965
  const arrowPath = pathBuilder().move(-3, 4).curve(-2.75, 2.5, 0, 0.25, 0.75, 0).curve(0, -0.25, -2.75, -2.5, -3, -4).scale(1.4).build();
28892
28966
  function Arrowhead(props) {
28893
28967
  var _props$color;
28894
- const [point] = useTransform([props.x, props.y]);
28968
+ const [point] = useTransformVectorsToPixels(props.tip);
28895
28969
  return /*#__PURE__*/React.createElement("g", {
28896
28970
  transform: `translate(${point[0]} ${point[1]}) rotate(${-props.angle})`
28897
28971
  }, /*#__PURE__*/React.createElement("g", {
@@ -28913,7 +28987,7 @@ function Vector(props) {
28913
28987
  tip,
28914
28988
  color
28915
28989
  } = props;
28916
- const [tailPx, tipPx] = useTransform(tail, tip);
28990
+ const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
28917
28991
  const direction = vec.sub(tip, tail);
28918
28992
  return /*#__PURE__*/React.createElement("g", {
28919
28993
  style: {
@@ -28924,8 +28998,7 @@ function Vector(props) {
28924
28998
  start: tailPx,
28925
28999
  end: tipPx
28926
29000
  }), /*#__PURE__*/React.createElement(Arrowhead, {
28927
- x: tip[0],
28928
- y: tip[1],
29001
+ tip: tip,
28929
29002
  angle: angleDegrees(direction)
28930
29003
  }));
28931
29004
  }
@@ -28943,12 +29016,16 @@ const MovableLine = props => {
28943
29016
  stroke = defaultStroke
28944
29017
  } = props;
28945
29018
  const midpoint = vec.midpoint(start, end);
28946
- const [startPtPx, endPtPx] = useTransform(start, end);
29019
+ const [startPtPx, endPtPx] = useTransformVectorsToPixels(start, end);
29020
+ const {
29021
+ graphDimensionsInPixels
29022
+ } = useGraphConfig();
28947
29023
  let startExtend = undefined;
28948
29024
  let endExtend = undefined;
28949
29025
  if (extend) {
28950
- startExtend = extend.start ? getRayIntersectionCoords(end, start, extend.range) : undefined;
28951
- endExtend = extend.end ? getRayIntersectionCoords(start, end, extend.range) : undefined;
29026
+ const trimmedRange = trimRange(extend.range, graphDimensionsInPixels);
29027
+ startExtend = extend.start ? getIntersectionOfRayWithBox(start, end, trimmedRange) : undefined;
29028
+ endExtend = extend.end ? getIntersectionOfRayWithBox(end, start, trimmedRange) : undefined;
28952
29029
  }
28953
29030
  const line = useRef(null);
28954
29031
  const {
@@ -29005,16 +29082,24 @@ const MovableLine = props => {
29005
29082
  color: stroke
29006
29083
  }));
29007
29084
  };
29008
-
29009
- const defaultGraphConfig = {
29010
- range: [[0, 1], [0, 1]],
29011
- snapStep: [1, 1],
29012
- markings: "none",
29013
- showTooltips: false
29014
- };
29015
- const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
29016
- function useGraphConfig() {
29017
- return React__default.useContext(GraphConfigContext);
29085
+ function trimRange(range, graphDimensionsInPixels) {
29086
+ const pixelsToTrim = 4;
29087
+ const [xRange, yRange] = range;
29088
+ const [pixelsWide, pixelsTall] = graphDimensionsInPixels;
29089
+ const graphUnitsPerPixelX = size(xRange) / pixelsWide;
29090
+ const graphUnitsPerPixelY = size(yRange) / pixelsTall;
29091
+ const graphUnitsToTrimX = pixelsToTrim * graphUnitsPerPixelX;
29092
+ const graphUnitsToTrimY = pixelsToTrim * graphUnitsPerPixelY;
29093
+ return [trim(xRange, graphUnitsToTrimX), trim(yRange, graphUnitsToTrimY)];
29094
+ }
29095
+ function trim(interval, amount) {
29096
+ if (size(interval) < amount * 2) {
29097
+ return [0, 0];
29098
+ }
29099
+ return [interval[0] + amount, interval[1] - amount];
29100
+ }
29101
+ function size(interval) {
29102
+ return interval[1] - interval[0];
29018
29103
  }
29019
29104
 
29020
29105
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
@@ -29047,13 +29132,13 @@ const StyledMovablePoint = props => {
29047
29132
  constrain: p => snap(snapStep, p)
29048
29133
  });
29049
29134
  const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""}`;
29050
- const [[x, y]] = useTransform(point);
29135
+ const [[x, y]] = useTransformVectorsToPixels(point);
29051
29136
  const [xMin, xMax] = range[0];
29052
29137
  const [yMin, yMax] = range[1];
29053
- const [[verticalStartX]] = useTransform([xMin, 0]);
29054
- const [[verticalEndX]] = useTransform([xMax, 0]);
29055
- const [[_, horizontalStartY]] = useTransform([0, yMin]);
29056
- const [[__, horizontalEndY]] = useTransform([0, yMax]);
29138
+ const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
29139
+ const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
29140
+ const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
29141
+ const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
29057
29142
  const showHairlines = dragging && markings !== "none";
29058
29143
  const hairlines = /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("line", {
29059
29144
  x1: verticalStartX,
@@ -29375,7 +29460,7 @@ const isRightAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
29375
29460
  */
29376
29461
  const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
29377
29462
  // Create a ray from the midpoint (inside angle) to the edge of the range
29378
- const rangeIntersectionPoint = getRayIntersectionCoords(midpoint, vertex, range);
29463
+ const rangeIntersectionPoint = getIntersectionOfRayWithBox(vertex, midpoint, range);
29379
29464
  let lineIntersectionCount = 0;
29380
29465
  polygonLines.forEach(line => linesIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
29381
29466
 
@@ -29498,23 +29583,19 @@ function AxisArrows() {
29498
29583
  const axisColor = "var(--mafs-fg)";
29499
29584
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrowhead, {
29500
29585
  color: axisColor,
29501
- x: xMin,
29502
- y: 0,
29586
+ tip: [xMin, 0],
29503
29587
  angle: 180
29504
29588
  }), /*#__PURE__*/React__default.createElement(Arrowhead, {
29505
29589
  color: axisColor,
29506
- x: xMax,
29507
- y: 0,
29590
+ tip: [xMax, 0],
29508
29591
  angle: 0
29509
29592
  }), /*#__PURE__*/React__default.createElement(Arrowhead, {
29510
29593
  color: axisColor,
29511
- x: 0,
29512
- y: yMin,
29594
+ tip: [0, yMin],
29513
29595
  angle: 270
29514
29596
  }), /*#__PURE__*/React__default.createElement(Arrowhead, {
29515
29597
  color: axisColor,
29516
- x: 0,
29517
- y: yMax,
29598
+ tip: [0, yMax],
29518
29599
  angle: 90
29519
29600
  }));
29520
29601
  }
@@ -29539,7 +29620,7 @@ const YGridTick = ({
29539
29620
  tickStep
29540
29621
  }) => {
29541
29622
  const pointOnAxis = [0, y];
29542
- const [[xPosition, yPosition]] = useTransform(pointOnAxis);
29623
+ const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
29543
29624
  return /*#__PURE__*/React.createElement("g", {
29544
29625
  className: "y-axis-ticks"
29545
29626
  }, /*#__PURE__*/React.createElement("line", {
@@ -29562,7 +29643,7 @@ const XGridTick = ({
29562
29643
  tickStep
29563
29644
  }) => {
29564
29645
  const pointOnAxis = [x, 0];
29565
- const [[xPosition, yPosition]] = useTransform(pointOnAxis);
29646
+ const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
29566
29647
  return /*#__PURE__*/React.createElement("g", {
29567
29648
  className: "x-axis-ticks"
29568
29649
  }, /*#__PURE__*/React.createElement("line", {
@@ -29783,97 +29864,26 @@ const styles$k = StyleSheet.create({
29783
29864
  }
29784
29865
  });
29785
29866
 
29786
- const colors$1 = {
29787
- grayLight: "#aaa",
29788
- basicBorderColor: "#ccc",
29789
- white: "#fff"
29790
- };
29791
- const triangleBeforeAfter = {
29792
- borderBottom: "9px solid transparent",
29793
- borderTop: "9px solid transparent",
29794
- content: '" "',
29795
- height: "0",
29796
- position: "absolute",
29797
- top: "0",
29798
- width: "0"
29867
+ const InfoTip$1 = props => {
29868
+ return /*#__PURE__*/React.createElement(Tooltip$1, {
29869
+ content: /*#__PURE__*/React.createElement(Body, {
29870
+ style: styles$j.body
29871
+ }, props.children),
29872
+ placement: "right"
29873
+ }, /*#__PURE__*/React.createElement(PhosphorIcon, {
29874
+ size: "small",
29875
+ icon: questionIcon,
29876
+ style: styles$j.spacingLeft
29877
+ }));
29799
29878
  };
29800
29879
  const styles$j = StyleSheet.create({
29801
- infoTip: {
29802
- display: "inline-block",
29803
- marginLeft: "5px",
29804
- position: "relative"
29880
+ spacingLeft: {
29881
+ marginInlineStart: spacing.xxxSmall_4
29805
29882
  },
29806
- infoTipContainer: {
29807
- position: "absolute",
29808
- top: "-12px",
29809
- left: "22px",
29810
- zIndex: 1000
29811
- },
29812
- infoTipTriangle: {
29813
- height: "10px",
29814
- left: "0",
29815
- position: "absolute",
29816
- top: "8px",
29817
- width: "0",
29818
- zIndex: 1,
29819
- ":before": _extends({}, triangleBeforeAfter, {
29820
- borderRight: "9px solid #bbb",
29821
- right: "0"
29822
- }),
29823
- ":after": _extends({}, triangleBeforeAfter, {
29824
- borderRight: `9px solid ${colors$1.white}`,
29825
- right: "-1px"
29826
- })
29827
- },
29828
- verticalShadow: {
29829
- border: `1px solid ${colors$1.basicBorderColor}`,
29830
- boxShadow: `0 1px 3px ${colors$1.basicBorderColor}`,
29831
- borderBottom: `1px solid ${colors$1.grayLight}`
29832
- },
29833
- infoTipContentContainer: {
29834
- background: colors$1.white,
29835
- padding: "5px 10px",
29836
- width: "240px"
29883
+ body: {
29884
+ padding: spacing.medium_16
29837
29885
  }
29838
29886
  });
29839
- class InfoTip$1 extends React.Component {
29840
- constructor(...args) {
29841
- super(...args);
29842
- this.state = {
29843
- hover: false
29844
- };
29845
- this.handleMouseEnter = () => {
29846
- this.setState({
29847
- hover: true
29848
- });
29849
- };
29850
- this.handleMouseLeave = () => {
29851
- this.setState({
29852
- hover: false
29853
- });
29854
- };
29855
- }
29856
- render() {
29857
- return /*#__PURE__*/React.createElement("div", {
29858
- className: css(styles$j.infoTip)
29859
- }, /*#__PURE__*/React.createElement(View, {
29860
- onMouseEnter: this.handleMouseEnter,
29861
- onMouseLeave: this.handleMouseLeave
29862
- }, /*#__PURE__*/React.createElement(PhosphorIcon, {
29863
- size: "small",
29864
- icon: questionIcon
29865
- })), /*#__PURE__*/React.createElement("div", {
29866
- className: css(styles$j.infoTipContainer),
29867
- style: {
29868
- display: this.state.hover ? "block" : "none"
29869
- }
29870
- }, /*#__PURE__*/React.createElement("div", {
29871
- className: css(styles$j.infoTipTriangle)
29872
- }), /*#__PURE__*/React.createElement("div", {
29873
- className: css(styles$j.verticalShadow, styles$j.infoTipContentContainer)
29874
- }, this.props.children)));
29875
- }
29876
- }
29877
29887
 
29878
29888
  /**
29879
29889
  * A wrapper around info-tip/info-tip.jsx that can be rendered on the
@@ -30713,9 +30723,7 @@ const getDefaultPoints = ({
30713
30723
  // Instead of updating to v5, using generic type to relate input and output types.
30714
30724
  function getGradableGraph(state, initialGraph) {
30715
30725
  if (!state.hasBeenInteractedWith) {
30716
- return {
30717
- type: initialGraph.type
30718
- };
30726
+ return _extends({}, initialGraph);
30719
30727
  }
30720
30728
  if (initialGraph.type === "linear-system" && state.type === "linear-system") {
30721
30729
  return _extends({}, initialGraph, {
@@ -30874,7 +30882,8 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30874
30882
  const MafsGraph = props => {
30875
30883
  const {
30876
30884
  state,
30877
- dispatch
30885
+ dispatch,
30886
+ labels
30878
30887
  } = props;
30879
30888
  const [width, height] = props.box;
30880
30889
  const prevState = useRef(state);
@@ -30903,7 +30912,11 @@ const MafsGraph = props => {
30903
30912
  range: state.range,
30904
30913
  snapStep: state.snapStep,
30905
30914
  markings: props.markings,
30906
- showTooltips: !!props.showTooltips
30915
+ showTooltips: !!props.showTooltips,
30916
+ graphDimensionsInPixels: props.box,
30917
+ width,
30918
+ height,
30919
+ labels
30907
30920
  }
30908
30921
  }, /*#__PURE__*/React.createElement(View, {
30909
30922
  style: {
@@ -30922,7 +30935,7 @@ const MafsGraph = props => {
30922
30935
  bottom: 0,
30923
30936
  left: 0
30924
30937
  }
30925
- }, /*#__PURE__*/React.createElement(Mafs, {
30938
+ }, props.markings === "graph" && /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(Mafs, {
30926
30939
  preserveAspectRatio: false,
30927
30940
  viewBox: {
30928
30941
  x: props.range[0],
@@ -32104,8 +32117,6 @@ class LegacyInteractiveGraph extends React.Component {
32104
32117
  graph: graph
32105
32118
  });
32106
32119
  };
32107
- this.getUserInput = () => InteractiveGraph.getUserInputFromProps(this.props);
32108
- this.simpleValidate = rubric => InteractiveGraph.validate(this.getUserInput(), rubric, this);
32109
32120
  this.focus = $.noop;
32110
32121
  }
32111
32122
  // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
@@ -32157,6 +32168,12 @@ class LegacyInteractiveGraph extends React.Component {
32157
32168
  areSegmentGraphsEqual(prevGraph, currentGraph) {
32158
32169
  return prevGraph.numSegments !== currentGraph.numSegments;
32159
32170
  }
32171
+ getUserInput() {
32172
+ return InteractiveGraph.getUserInputFromProps(this.props);
32173
+ }
32174
+ simpleValidate(rubric) {
32175
+ return InteractiveGraph.validate(this.getUserInput(), rubric, this);
32176
+ }
32160
32177
  render() {
32161
32178
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
32162
32179
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
@@ -32238,10 +32255,10 @@ class InteractiveGraph extends React.Component {
32238
32255
  }
32239
32256
  throw new PerseusError("Cannot getUserInput from a graph that has never rendered", Errors.NotAllowed);
32240
32257
  };
32241
- this.simpleValidate = rubric => {
32242
- var _this$getUserInput;
32243
- return InteractiveGraph.validate((_this$getUserInput = this.getUserInput) == null ? void 0 : _this$getUserInput.call(this), rubric, this);
32244
- };
32258
+ }
32259
+ simpleValidate(rubric) {
32260
+ var _this$getUserInput;
32261
+ return InteractiveGraph.validate((_this$getUserInput = this.getUserInput) == null ? void 0 : _this$getUserInput.call(this), rubric, this);
32245
32262
  }
32246
32263
  render() {
32247
32264
  var _this$props$apiOption;
@@ -43037,7 +43054,7 @@ const init = function init(options) {
43037
43054
 
43038
43055
  // This file is processed by a Rollup plugin (replace) to inject the production
43039
43056
  const libName = "@khanacademy/perseus";
43040
- const libVersion = "22.0.2";
43057
+ const libVersion = "22.1.0";
43041
43058
  addLibraryVersionToPerseusDebug(libName, libVersion);
43042
43059
 
43043
43060
  const apiVersion = {