@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.
package/dist/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/index.js CHANGED
@@ -32,8 +32,8 @@ var caretUp = require('@phosphor-icons/core/assets/regular/caret-up.svg');
32
32
  var debounce$1 = require('lodash.debounce');
33
33
  var mafs = require('mafs');
34
34
  var wonderBlocksIcon = require('@khanacademy/wonder-blocks-icon');
35
- var questionIcon = require('@phosphor-icons/core/regular/question.svg');
36
35
  var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
36
+ var questionIcon = require('@phosphor-icons/core/regular/question.svg');
37
37
  var createReactClass = require('create-react-class');
38
38
  var Switch = require('@khanacademy/wonder-blocks-switch');
39
39
  var Pill = require('@khanacademy/wonder-blocks-pill');
@@ -28761,6 +28761,93 @@ class Graph extends React__namespace.Component {
28761
28761
  }
28762
28762
  }
28763
28763
 
28764
+ const defaultGraphConfig = {
28765
+ range: [[0, 1], [0, 1]],
28766
+ snapStep: [1, 1],
28767
+ markings: "none",
28768
+ showTooltips: false,
28769
+ graphDimensionsInPixels: [1, 1],
28770
+ width: 0,
28771
+ height: 0,
28772
+ labels: []
28773
+ };
28774
+ const GraphConfigContext = /*#__PURE__*/React.createContext(defaultGraphConfig);
28775
+ function useGraphConfig() {
28776
+ return React__default["default"].useContext(GraphConfigContext);
28777
+ }
28778
+
28779
+ const matrixBuilder = mafs.vec.matrixBuilder;
28780
+ // When converting vectors, we don't need to translate because a vector
28781
+ // is like a pair of x and y distances, which are not bound to specific graph locations.
28782
+ function vectorsToPixels(vectors, graphState) {
28783
+ let translation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [0, 0];
28784
+ const {
28785
+ range,
28786
+ width,
28787
+ height
28788
+ } = graphState;
28789
+ const [[xMin, xMax], [yMin, yMax]] = range;
28790
+ const transformToPx = matrixBuilder().translate(...translation).scale(width / (xMax - xMin), -height / (yMax - yMin)).get();
28791
+ return vectors.map(p => mafs.vec.transform(p, transformToPx));
28792
+ }
28793
+
28794
+ // When converting points, we translate so the pixel point ends up in the right location.
28795
+ // This is because points are specific locations on graphs, unlike vectors.
28796
+ function pointToPixel(point, graphState) {
28797
+ const [[xMin], [, yMax]] = graphState.range;
28798
+ const [a, b] = [-xMin, -yMax];
28799
+ return vectorsToPixels([point], graphState, [a, b])[0];
28800
+ }
28801
+ const useTransformVectorsToPixels = function () {
28802
+ const graphState = useGraphConfig();
28803
+ for (var _len = arguments.length, vectors = new Array(_len), _key = 0; _key < _len; _key++) {
28804
+ vectors[_key] = arguments[_key];
28805
+ }
28806
+ return vectorsToPixels(vectors, graphState);
28807
+ };
28808
+
28809
+ function AxisLabels() {
28810
+ const {
28811
+ range,
28812
+ labels,
28813
+ width,
28814
+ height
28815
+ } = useGraphConfig();
28816
+ const yAxisLabelLocation = [0, range[1][1]];
28817
+ const xAxisLabelLocation = [range[0][1], 0];
28818
+ const [xAxisLabelText, yAxisLabelText] = labels;
28819
+ const graphInfo = {
28820
+ range,
28821
+ width,
28822
+ height
28823
+ };
28824
+ const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
28825
+ // The default location for the y-axis-label is at the maximum y point
28826
+ // This is just underneath the tick line for that point. The -24 moves the
28827
+ // label up one grid square, so it sits on top of the graph
28828
+ const [x2, y2] = mafs.vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
28829
+ const {
28830
+ TeX
28831
+ } = getDependencies();
28832
+ return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("span", {
28833
+ style: {
28834
+ position: "absolute",
28835
+ left: x1,
28836
+ top: y1,
28837
+ fontSize: "14px",
28838
+ transform: "translate(7px, -50%)"
28839
+ }
28840
+ }, /*#__PURE__*/React__default["default"].createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default["default"].createElement("span", {
28841
+ style: {
28842
+ position: "absolute",
28843
+ left: x2,
28844
+ top: y2,
28845
+ fontSize: "14px",
28846
+ transform: "translate(-50%, 0px)"
28847
+ }
28848
+ }, /*#__PURE__*/React__default["default"].createElement(TeX, null, yAxisLabelText)));
28849
+ }
28850
+
28764
28851
  // TODO(FEI-4010): Remove `Perseus` prefix for all types here
28765
28852
  // TODO(FEI-4011): Use types generated by https://github.com/jaredly/generate-perseus-flowtypes
28766
28853
 
@@ -29052,29 +29139,17 @@ function snap(snapStep, point) {
29052
29139
  return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
29053
29140
  }
29054
29141
 
29055
- const useTransform = function () {
29056
- const {
29057
- viewTransform,
29058
- userTransform
29059
- } = mafs.useTransformContext();
29060
- const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
29061
- for (var _len = arguments.length, points = new Array(_len), _key = 0; _key < _len; _key++) {
29062
- points[_key] = arguments[_key];
29063
- }
29064
- const transformedPoints = points.map(point => mafs.vec.transform(point, transformToPx));
29065
- return transformedPoints;
29066
- };
29067
-
29068
29142
  /**
29069
- * Given two points, find the tips that extends through the points to the edge of the range.
29070
- * @param collinearPoint - The point that the line passes through. Needed to establish slope.
29071
- * @param extendFrom - The point that the line extends from to the edge of the graph.
29143
+ * Given a ray and a rectangular box, find the point where the ray intersects
29144
+ * the edge of the box. Assumes the `initialPoint` is inside the box.
29145
+ * @param initialPoint - The starting point of the ray.
29146
+ * @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
29147
+ * @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
29072
29148
  */
29073
- const getRayIntersectionCoords = (collinearPoint, extendFrom, range) => {
29074
- // edges of the graph
29075
- const [[xMin, xMax], [yMin, yMax]] = range;
29076
- const [aX, aY] = collinearPoint;
29077
- const [bX, bY] = extendFrom;
29149
+ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
29150
+ const [[xMin, xMax], [yMin, yMax]] = box;
29151
+ const [aX, aY] = throughPoint;
29152
+ const [bX, bY] = initialPoint;
29078
29153
  const yDiff = bY - aY;
29079
29154
  const xDiff = bX - aX;
29080
29155
  const slope = yDiff / xDiff;
@@ -29176,7 +29251,7 @@ function scaleCommandBy(scaleFactor) {
29176
29251
  // don't want that.
29177
29252
  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();
29178
29253
  function Arrowhead(props) {
29179
- const [point] = useTransform([props.x, props.y]);
29254
+ const [point] = useTransformVectorsToPixels(props.tip);
29180
29255
  return /*#__PURE__*/React__namespace.createElement("g", {
29181
29256
  transform: `translate(${point[0]} ${point[1]}) rotate(${-props.angle})`
29182
29257
  }, /*#__PURE__*/React__namespace.createElement("g", {
@@ -29198,7 +29273,7 @@ function Vector(props) {
29198
29273
  tip,
29199
29274
  color
29200
29275
  } = props;
29201
- const [tailPx, tipPx] = useTransform(tail, tip);
29276
+ const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
29202
29277
  const direction = mafs.vec.sub(tip, tail);
29203
29278
  return /*#__PURE__*/React__namespace.createElement("g", {
29204
29279
  style: {
@@ -29209,8 +29284,7 @@ function Vector(props) {
29209
29284
  start: tailPx,
29210
29285
  end: tipPx
29211
29286
  }), /*#__PURE__*/React__namespace.createElement(Arrowhead, {
29212
- x: tip[0],
29213
- y: tip[1],
29287
+ tip: tip,
29214
29288
  angle: angleDegrees(direction)
29215
29289
  }));
29216
29290
  }
@@ -29229,12 +29303,16 @@ const MovableLine = props => {
29229
29303
  stroke = defaultStroke
29230
29304
  } = props;
29231
29305
  const midpoint = mafs.vec.midpoint(start, end);
29232
- const [startPtPx, endPtPx] = useTransform(start, end);
29306
+ const [startPtPx, endPtPx] = useTransformVectorsToPixels(start, end);
29307
+ const {
29308
+ graphDimensionsInPixels
29309
+ } = useGraphConfig();
29233
29310
  let startExtend = undefined;
29234
29311
  let endExtend = undefined;
29235
29312
  if (extend) {
29236
- startExtend = extend.start ? getRayIntersectionCoords(end, start, extend.range) : undefined;
29237
- endExtend = extend.end ? getRayIntersectionCoords(start, end, extend.range) : undefined;
29313
+ const trimmedRange = trimRange(extend.range, graphDimensionsInPixels);
29314
+ startExtend = extend.start ? getIntersectionOfRayWithBox(start, end, trimmedRange) : undefined;
29315
+ endExtend = extend.end ? getIntersectionOfRayWithBox(end, start, trimmedRange) : undefined;
29238
29316
  }
29239
29317
  const line = React.useRef(null);
29240
29318
  const {
@@ -29291,16 +29369,24 @@ const MovableLine = props => {
29291
29369
  color: stroke
29292
29370
  }));
29293
29371
  };
29294
-
29295
- const defaultGraphConfig = {
29296
- range: [[0, 1], [0, 1]],
29297
- snapStep: [1, 1],
29298
- markings: "none",
29299
- showTooltips: false
29300
- };
29301
- const GraphConfigContext = /*#__PURE__*/React.createContext(defaultGraphConfig);
29302
- function useGraphConfig() {
29303
- return React__default["default"].useContext(GraphConfigContext);
29372
+ function trimRange(range, graphDimensionsInPixels) {
29373
+ const pixelsToTrim = 4;
29374
+ const [xRange, yRange] = range;
29375
+ const [pixelsWide, pixelsTall] = graphDimensionsInPixels;
29376
+ const graphUnitsPerPixelX = size(xRange) / pixelsWide;
29377
+ const graphUnitsPerPixelY = size(yRange) / pixelsTall;
29378
+ const graphUnitsToTrimX = pixelsToTrim * graphUnitsPerPixelX;
29379
+ const graphUnitsToTrimY = pixelsToTrim * graphUnitsPerPixelY;
29380
+ return [trim(xRange, graphUnitsToTrimX), trim(yRange, graphUnitsToTrimY)];
29381
+ }
29382
+ function trim(interval, amount) {
29383
+ if (size(interval) < amount * 2) {
29384
+ return [0, 0];
29385
+ }
29386
+ return [interval[0] + amount, interval[1] - amount];
29387
+ }
29388
+ function size(interval) {
29389
+ return interval[1] - interval[0];
29304
29390
  }
29305
29391
 
29306
29392
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
@@ -29335,13 +29421,13 @@ const StyledMovablePoint = props => {
29335
29421
  constrain: p => snap(snapStep, p)
29336
29422
  });
29337
29423
  const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""}`;
29338
- const [[x, y]] = useTransform(point);
29424
+ const [[x, y]] = useTransformVectorsToPixels(point);
29339
29425
  const [xMin, xMax] = range[0];
29340
29426
  const [yMin, yMax] = range[1];
29341
- const [[verticalStartX]] = useTransform([xMin, 0]);
29342
- const [[verticalEndX]] = useTransform([xMax, 0]);
29343
- const [[_, horizontalStartY]] = useTransform([0, yMin]);
29344
- const [[__, horizontalEndY]] = useTransform([0, yMax]);
29427
+ const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
29428
+ const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
29429
+ const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
29430
+ const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
29345
29431
  const showHairlines = dragging && markings !== "none";
29346
29432
  const hairlines = /*#__PURE__*/React__namespace.createElement("g", null, /*#__PURE__*/React__namespace.createElement("line", {
29347
29433
  x1: verticalStartX,
@@ -29669,7 +29755,7 @@ const isRightAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
29669
29755
  */
29670
29756
  const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
29671
29757
  // Create a ray from the midpoint (inside angle) to the edge of the range
29672
- const rangeIntersectionPoint = getRayIntersectionCoords(midpoint, vertex, range);
29758
+ const rangeIntersectionPoint = getIntersectionOfRayWithBox(vertex, midpoint, range);
29673
29759
  let lineIntersectionCount = 0;
29674
29760
  polygonLines.forEach(line => linesIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
29675
29761
 
@@ -29794,23 +29880,19 @@ function AxisArrows() {
29794
29880
  const axisColor = "var(--mafs-fg)";
29795
29881
  return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
29796
29882
  color: axisColor,
29797
- x: xMin,
29798
- y: 0,
29883
+ tip: [xMin, 0],
29799
29884
  angle: 180
29800
29885
  }), /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
29801
29886
  color: axisColor,
29802
- x: xMax,
29803
- y: 0,
29887
+ tip: [xMax, 0],
29804
29888
  angle: 0
29805
29889
  }), /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
29806
29890
  color: axisColor,
29807
- x: 0,
29808
- y: yMin,
29891
+ tip: [0, yMin],
29809
29892
  angle: 270
29810
29893
  }), /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
29811
29894
  color: axisColor,
29812
- x: 0,
29813
- y: yMax,
29895
+ tip: [0, yMax],
29814
29896
  angle: 90
29815
29897
  }));
29816
29898
  }
@@ -29836,7 +29918,7 @@ const YGridTick = _ref => {
29836
29918
  tickStep
29837
29919
  } = _ref;
29838
29920
  const pointOnAxis = [0, y];
29839
- const [[xPosition, yPosition]] = useTransform(pointOnAxis);
29921
+ const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
29840
29922
  return /*#__PURE__*/React__namespace.createElement("g", {
29841
29923
  className: "y-axis-ticks"
29842
29924
  }, /*#__PURE__*/React__namespace.createElement("line", {
@@ -29860,7 +29942,7 @@ const XGridTick = _ref2 => {
29860
29942
  tickStep
29861
29943
  } = _ref2;
29862
29944
  const pointOnAxis = [x, 0];
29863
- const [[xPosition, yPosition]] = useTransform(pointOnAxis);
29945
+ const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
29864
29946
  return /*#__PURE__*/React__namespace.createElement("g", {
29865
29947
  className: "x-axis-ticks"
29866
29948
  }, /*#__PURE__*/React__namespace.createElement("line", {
@@ -30082,96 +30164,26 @@ const styles$k = aphrodite.StyleSheet.create({
30082
30164
  }
30083
30165
  });
30084
30166
 
30085
- const colors$1 = {
30086
- grayLight: "#aaa",
30087
- basicBorderColor: "#ccc",
30088
- white: "#fff"
30089
- };
30090
- const triangleBeforeAfter = {
30091
- borderBottom: "9px solid transparent",
30092
- borderTop: "9px solid transparent",
30093
- content: '" "',
30094
- height: "0",
30095
- position: "absolute",
30096
- top: "0",
30097
- width: "0"
30167
+ const InfoTip$1 = props => {
30168
+ return /*#__PURE__*/React__namespace.createElement(Tooltip__default["default"], {
30169
+ content: /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.Body, {
30170
+ style: styles$j.body
30171
+ }, props.children),
30172
+ placement: "right"
30173
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksIcon.PhosphorIcon, {
30174
+ size: "small",
30175
+ icon: questionIcon__default["default"],
30176
+ style: styles$j.spacingLeft
30177
+ }));
30098
30178
  };
30099
30179
  const styles$j = aphrodite.StyleSheet.create({
30100
- infoTip: {
30101
- display: "inline-block",
30102
- marginLeft: "5px",
30103
- position: "relative"
30180
+ spacingLeft: {
30181
+ marginInlineStart: wonderBlocksTokens.spacing.xxxSmall_4
30104
30182
  },
30105
- infoTipContainer: {
30106
- position: "absolute",
30107
- top: "-12px",
30108
- left: "22px",
30109
- zIndex: 1000
30110
- },
30111
- infoTipTriangle: {
30112
- height: "10px",
30113
- left: "0",
30114
- position: "absolute",
30115
- top: "8px",
30116
- width: "0",
30117
- zIndex: 1,
30118
- ":before": {
30119
- ...triangleBeforeAfter,
30120
- borderRight: "9px solid #bbb",
30121
- right: "0"
30122
- },
30123
- ":after": {
30124
- ...triangleBeforeAfter,
30125
- borderRight: `9px solid ${colors$1.white}`,
30126
- right: "-1px"
30127
- }
30128
- },
30129
- verticalShadow: {
30130
- border: `1px solid ${colors$1.basicBorderColor}`,
30131
- boxShadow: `0 1px 3px ${colors$1.basicBorderColor}`,
30132
- borderBottom: `1px solid ${colors$1.grayLight}`
30133
- },
30134
- infoTipContentContainer: {
30135
- background: colors$1.white,
30136
- padding: "5px 10px",
30137
- width: "240px"
30183
+ body: {
30184
+ padding: wonderBlocksTokens.spacing.medium_16
30138
30185
  }
30139
30186
  });
30140
- class InfoTip$1 extends React__namespace.Component {
30141
- state = {
30142
- hover: false
30143
- };
30144
- handleMouseEnter = () => {
30145
- this.setState({
30146
- hover: true
30147
- });
30148
- };
30149
- handleMouseLeave = () => {
30150
- this.setState({
30151
- hover: false
30152
- });
30153
- };
30154
- render() {
30155
- return /*#__PURE__*/React__namespace.createElement("div", {
30156
- className: aphrodite.css(styles$j.infoTip)
30157
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30158
- onMouseEnter: this.handleMouseEnter,
30159
- onMouseLeave: this.handleMouseLeave
30160
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksIcon.PhosphorIcon, {
30161
- size: "small",
30162
- icon: questionIcon__default["default"]
30163
- })), /*#__PURE__*/React__namespace.createElement("div", {
30164
- className: aphrodite.css(styles$j.infoTipContainer),
30165
- style: {
30166
- display: this.state.hover ? "block" : "none"
30167
- }
30168
- }, /*#__PURE__*/React__namespace.createElement("div", {
30169
- className: aphrodite.css(styles$j.infoTipTriangle)
30170
- }), /*#__PURE__*/React__namespace.createElement("div", {
30171
- className: aphrodite.css(styles$j.verticalShadow, styles$j.infoTipContentContainer)
30172
- }, this.props.children)));
30173
- }
30174
- }
30175
30187
 
30176
30188
  /**
30177
30189
  * A wrapper around info-tip/info-tip.jsx that can be rendered on the
@@ -31028,7 +31040,7 @@ const getDefaultPoints = _ref => {
31028
31040
  function getGradableGraph(state, initialGraph) {
31029
31041
  if (!state.hasBeenInteractedWith) {
31030
31042
  return {
31031
- type: initialGraph.type
31043
+ ...initialGraph
31032
31044
  };
31033
31045
  }
31034
31046
  if (initialGraph.type === "linear-system" && state.type === "linear-system") {
@@ -31195,7 +31207,8 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
31195
31207
  const MafsGraph = props => {
31196
31208
  const {
31197
31209
  state,
31198
- dispatch
31210
+ dispatch,
31211
+ labels
31199
31212
  } = props;
31200
31213
  const [width, height] = props.box;
31201
31214
  const prevState = React.useRef(state);
@@ -31224,7 +31237,11 @@ const MafsGraph = props => {
31224
31237
  range: state.range,
31225
31238
  snapStep: state.snapStep,
31226
31239
  markings: props.markings,
31227
- showTooltips: !!props.showTooltips
31240
+ showTooltips: !!props.showTooltips,
31241
+ graphDimensionsInPixels: props.box,
31242
+ width,
31243
+ height,
31244
+ labels
31228
31245
  }
31229
31246
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31230
31247
  style: {
@@ -31243,7 +31260,7 @@ const MafsGraph = props => {
31243
31260
  bottom: 0,
31244
31261
  left: 0
31245
31262
  }
31246
- }, /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
31263
+ }, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(AxisLabels, null), /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
31247
31264
  preserveAspectRatio: false,
31248
31265
  viewBox: {
31249
31266
  x: props.range[0],
@@ -32470,8 +32487,12 @@ class LegacyInteractiveGraph extends React__namespace.Component {
32470
32487
  graph: graph
32471
32488
  });
32472
32489
  };
32473
- getUserInput = () => InteractiveGraph.getUserInputFromProps(this.props);
32474
- simpleValidate = rubric => InteractiveGraph.validate(this.getUserInput(), rubric, this);
32490
+ getUserInput() {
32491
+ return InteractiveGraph.getUserInputFromProps(this.props);
32492
+ }
32493
+ simpleValidate(rubric) {
32494
+ return InteractiveGraph.validate(this.getUserInput(), rubric, this);
32495
+ }
32475
32496
  focus = $__default["default"].noop;
32476
32497
  render() {
32477
32498
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
@@ -32550,7 +32571,9 @@ class InteractiveGraph extends React__namespace.Component {
32550
32571
  }
32551
32572
  throw new PerseusError("Cannot getUserInput from a graph that has never rendered", Errors.NotAllowed);
32552
32573
  };
32553
- simpleValidate = rubric => InteractiveGraph.validate(this.getUserInput?.(), rubric, this);
32574
+ simpleValidate(rubric) {
32575
+ return InteractiveGraph.validate(this.getUserInput?.(), rubric, this);
32576
+ }
32554
32577
  render() {
32555
32578
  // Mafs shim
32556
32579
  if (this.props.apiOptions?.flags?.["mafs"]?.[this.props.graph.type]) {
@@ -43351,7 +43374,7 @@ const init = function (options) {
43351
43374
 
43352
43375
  // This file is processed by a Rollup plugin (replace) to inject the production
43353
43376
  const libName = "@khanacademy/perseus";
43354
- const libVersion = "22.0.2";
43377
+ const libVersion = "22.1.0";
43355
43378
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
43356
43379
 
43357
43380
  const apiVersion = {