@khanacademy/perseus 47.0.1 → 48.0.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.js CHANGED
@@ -2412,9 +2412,6 @@ function scoreExpression(userInput, rubric, strings, locale) {
2412
2412
  // We matched a graded answer form, so we can now tell the user
2413
2413
  // whether their input was correct or incorrect, and hand out
2414
2414
  // points accordingly
2415
- // TODO(eater): Seems silly to translate result to this
2416
- // invalid/points thing and immediately translate it back in
2417
- // ItemRenderer.scoreInput()
2418
2415
  return {
2419
2416
  type: "points",
2420
2417
  earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
@@ -3363,7 +3360,7 @@ const supportsPassiveEvents = () => {
3363
3360
  window.addEventListener("testPassive", null, opts);
3364
3361
  // @ts-expect-error - TS2769 - No overload matches this call.
3365
3362
  window.removeEventListener("testPassive", null, opts);
3366
- } catch (e) {
3363
+ } catch {
3367
3364
  // Intentionally left empty!
3368
3365
  }
3369
3366
  return supportsPassive;
@@ -3604,7 +3601,9 @@ function changeViewportTag(contentString, callback) {
3604
3601
  // finished resetting.
3605
3602
  // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
3606
3603
  // eslint-disable-next-line no-restricted-syntax
3607
- callback && setTimeout(callback, 0);
3604
+ if (callback != null) {
3605
+ setTimeout(callback, 0);
3606
+ }
3608
3607
  }, 0);
3609
3608
  }
3610
3609
 
@@ -16964,7 +16963,7 @@ class ImageLoader extends React__namespace.Component {
16964
16963
  onKeyUp = e => {
16965
16964
  // 13 is enter key, 32 is space key
16966
16965
  if (e.keyCode === 13 || e.keyCode === 32) {
16967
- imgProps.onClick && imgProps.onClick(e);
16966
+ imgProps.onClick?.(e);
16968
16967
  }
16969
16968
  };
16970
16969
  onKeyDown = e => {
@@ -17033,7 +17032,7 @@ const doJSONP = function (url, options) {
17033
17032
 
17034
17033
  // A cleanup function to run when we're done.
17035
17034
  function cleanup() {
17036
- document.head && document.head.removeChild(script);
17035
+ document.head?.removeChild(script);
17037
17036
  delete window[options.callbackName];
17038
17037
  }
17039
17038
 
@@ -17057,7 +17056,7 @@ const doJSONP = function (url, options) {
17057
17056
  });
17058
17057
 
17059
17058
  // Insert the script to start the download.
17060
- document.head && document.head.appendChild(script);
17059
+ document.head?.appendChild(script);
17061
17060
  };
17062
17061
 
17063
17062
  // For offline exercises in the mobile app, we download the graphie data
@@ -17396,7 +17395,7 @@ class SvgImage extends React__namespace.Component {
17396
17395
  $image.width() < this.props.width || this.props.zoomToFullSizeOnMobile) {
17397
17396
  ZoomService.handleZoomClick(e, this.props.zoomToFullSizeOnMobile);
17398
17397
  }
17399
- this.props.trackInteraction && this.props.trackInteraction();
17398
+ this.props.trackInteraction?.();
17400
17399
  };
17401
17400
  handleUpdate = status => {
17402
17401
  this.props.onUpdate();
@@ -19803,8 +19802,8 @@ class WidgetContainer extends React__namespace.Component {
19803
19802
  };
19804
19803
 
19805
19804
  // Note: if you add more props here, please consider whether or not
19806
- // it should be auto-serialized (e.g. used in scoreInput()). See
19807
- // widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
19805
+ // it should be auto-serialized.
19806
+ // See widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
19808
19807
 
19809
19808
  // We default to an empty object for style instead of null
19810
19809
  // because of a strange bug where the static styles aren't applied
@@ -21100,15 +21099,6 @@ class Renderer extends React__namespace.Component {
21100
21099
  return combinedScore;
21101
21100
  }
21102
21101
 
21103
- /**
21104
- * @deprecated use scorePerseusItem
21105
- */
21106
- guessAndScore = () => {
21107
- const totalGuess = this.getUserInput();
21108
- const totalScore = this.score();
21109
- return [totalGuess, totalScore];
21110
- };
21111
-
21112
21102
  // TranslationLinter callback
21113
21103
  handletranslationLintErrors = lintErrors => {
21114
21104
  if (!this._isMounted) {
@@ -21786,11 +21776,11 @@ class SimpleKeypadInput extends React__namespace.Component {
21786
21776
  keypadType: mathInput.KeypadType.FRACTION
21787
21777
  }, () => {
21788
21778
  if (_this._isMounted) {
21789
- onFocus && onFocus();
21779
+ onFocus?.();
21790
21780
  }
21791
21781
  });
21792
21782
  } else {
21793
- onFocus && onFocus();
21783
+ onFocus?.();
21794
21784
  }
21795
21785
  },
21796
21786
  value: value == null ? "" : "" + value
@@ -22029,9 +22019,6 @@ function scoreInputNumber(userInput, rubric, strings) {
22029
22019
  // If `currentValue` is not TeX, this should be a no-op.
22030
22020
  const currentValue = ParseTex$1(userInput.currentValue);
22031
22021
  const result = val(currentValue);
22032
-
22033
- // TODO(eater): Seems silly to translate result to this invalid/points
22034
- // thing and immediately translate it back in ItemRenderer.scoreInput()
22035
22022
  if (result.empty) {
22036
22023
  return {
22037
22024
  type: "invalid",
@@ -22395,10 +22382,6 @@ function scoreNumericInput(userInput, rubric, strings) {
22395
22382
  message: matchedAnswer?.message ?? null,
22396
22383
  guess: localValue
22397
22384
  };
22398
-
22399
- // TODO(eater): Seems silly to translate result to this
22400
- // invalid/points thing and immediately translate it
22401
- // back in ItemRenderer.scoreInput()
22402
22385
  if (result.empty) {
22403
22386
  return {
22404
22387
  type: "invalid",
@@ -25255,7 +25238,7 @@ const scrollElementToOffset = function (elementOrWindow, offset) {
25255
25238
  // TODO(jeff, WEB-1378): Use Wonder Blocks Timing API
25256
25239
  // eslint-disable-next-line no-restricted-syntax
25257
25240
  clearInterval(scrollInterval);
25258
- callback && callback();
25241
+ callback?.();
25259
25242
  }
25260
25243
  }, tickLength);
25261
25244
  };
@@ -27317,7 +27300,7 @@ class CSProgram extends React__namespace.Component {
27317
27300
  let data = {};
27318
27301
  try {
27319
27302
  data = JSON.parse(e.originalEvent.data);
27320
- } catch (err) {
27303
+ } catch {
27321
27304
  return;
27322
27305
  }
27323
27306
  if (___default["default"].isUndefined(data.testsPassed)) {
@@ -28344,7 +28327,6 @@ class GradedGroupSet extends React__namespace.Component {
28344
28327
  currentGroup: 0
28345
28328
  };
28346
28329
  shouldComponentUpdate(nextProps, nextState) {
28347
- nextProps.gradedGroups;
28348
28330
  return nextProps !== this.props || nextState !== this.state;
28349
28331
  }
28350
28332
  change = (() => {
@@ -29868,7 +29850,7 @@ class Iframe extends React__namespace.Component {
29868
29850
  let data = {};
29869
29851
  try {
29870
29852
  data = JSON.parse(e.originalEvent.data);
29871
- } catch (err) {
29853
+ } catch {
29872
29854
  return;
29873
29855
  }
29874
29856
  if (___default["default"].isUndefined(data.testsPassed)) {
@@ -30963,10 +30945,10 @@ class Graph extends React__namespace.Component {
30963
30945
  this.props.onMouseDown(coord);
30964
30946
  }, this) : undefined;
30965
30947
  const onMouseOver = $instructionsWrapper ? function () {
30966
- $instructionsWrapper && $instructionsWrapper.css("opacity", invisible);
30948
+ $instructionsWrapper?.css("opacity", invisible);
30967
30949
  } : undefined;
30968
30950
  const onMouseOut = $instructionsWrapper ? function () {
30969
- $instructionsWrapper && $instructionsWrapper.css("opacity", visible);
30951
+ $instructionsWrapper?.css("opacity", visible);
30970
30952
  } : undefined;
30971
30953
  /* eslint-enable indent */
30972
30954
 
@@ -31679,6 +31661,7 @@ function replaceOutsideTeX(mathString) {
31679
31661
  const parsed = mathOnlyParser(mathString);
31680
31662
  let result = "";
31681
31663
  for (const piece of parsed) {
31664
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
31682
31665
  piece.type === "math" ? result += "$" + piece.content + "$" : piece.type === "specialCharacter" ? result += escapeIfUnescaped(piece.content) : result += piece.content;
31683
31666
  }
31684
31667
  return `\\text{${result}}`;
@@ -34650,42 +34633,64 @@ function renderPointGraph(state, dispatch) {
34650
34633
  };
34651
34634
  }
34652
34635
  function PointGraph(props) {
34653
- const numPoints = props.graphState.numPoints;
34636
+ const {
34637
+ numPoints
34638
+ } = props.graphState;
34639
+ const graphConfig = useGraphConfig();
34640
+ const pointsRef = React__namespace.useRef([]);
34641
+
34642
+ // Dimensions to build the graph overlay for Unlimited Point.
34643
+ const {
34644
+ range: [x, y]
34645
+ } = graphConfig;
34646
+ const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
34647
+
34648
+ // This useEffect is to handle the focus snapping for Unlimited Point.
34649
+ React__namespace.useEffect(() => {
34650
+ const focusedIndex = props.graphState.focusedPointIndex;
34651
+ if (focusedIndex != null) {
34652
+ pointsRef.current[focusedIndex]?.focus();
34653
+ }
34654
+ }, [props.graphState.focusedPointIndex, pointsRef]);
34655
+ const statefulProps = {
34656
+ ...props,
34657
+ graphConfig,
34658
+ pointsRef,
34659
+ top,
34660
+ left
34661
+ };
34654
34662
  if (numPoints === "unlimited") {
34655
- return UnlimitedPointGraph(props);
34663
+ return UnlimitedPointGraph(statefulProps);
34656
34664
  }
34657
- return LimitedPointGraph(props);
34665
+ return LimitedPointGraph(statefulProps);
34658
34666
  }
34659
- function LimitedPointGraph(props) {
34667
+ function LimitedPointGraph(statefulProps) {
34660
34668
  const {
34661
34669
  dispatch
34662
- } = props;
34663
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34670
+ } = statefulProps;
34671
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, statefulProps.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34664
34672
  key: i,
34665
34673
  point: point,
34666
34674
  sequenceNumber: i + 1,
34667
34675
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
34668
34676
  })));
34669
34677
  }
34670
- function UnlimitedPointGraph(props) {
34678
+ function UnlimitedPointGraph(statefulProps) {
34671
34679
  const {
34672
- dispatch
34673
- } = props;
34674
- const graphConfig = useGraphConfig();
34680
+ dispatch,
34681
+ graphConfig,
34682
+ pointsRef,
34683
+ top,
34684
+ left
34685
+ } = statefulProps;
34675
34686
  const {
34676
- range: [[minX, maxX], [minY, maxY]]
34687
+ coords
34688
+ } = statefulProps.graphState;
34689
+ const {
34690
+ graphDimensionsInPixels
34677
34691
  } = graphConfig;
34678
- const width = maxX - minX;
34679
- const height = maxY - minY;
34680
- const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
34681
- const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
34682
- const itemsRef = React__namespace.useRef([]);
34683
- React__namespace.useEffect(() => {
34684
- const focusedIndex = props.graphState.focusedPointIndex;
34685
- if (focusedIndex != null) {
34686
- itemsRef.current[focusedIndex]?.focus();
34687
- }
34688
- }, [props.graphState.focusedPointIndex, itemsRef]);
34692
+ const widthPx = graphDimensionsInPixels[0];
34693
+ const heightPx = graphDimensionsInPixels[1];
34689
34694
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
34690
34695
  style: {
34691
34696
  fill: "rgba(0,0,0,0)",
@@ -34702,13 +34707,13 @@ function UnlimitedPointGraph(props) {
34702
34707
  const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
34703
34708
  dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
34704
34709
  }
34705
- }), props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34710
+ }), coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34706
34711
  key: i,
34707
34712
  point: point,
34708
34713
  sequenceNumber: i + 1,
34709
34714
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
34710
34715
  ref: ref => {
34711
- itemsRef.current[i] = ref;
34716
+ pointsRef.current[i] = ref;
34712
34717
  },
34713
34718
  onFocus: () => {
34714
34719
  dispatch(actions.pointGraph.focusPoint(i));
@@ -34759,36 +34764,42 @@ function renderPolygonGraph(state, dispatch) {
34759
34764
  interactiveElementsDescription: null
34760
34765
  };
34761
34766
  }
34762
- const LimitedPolygonGraph = props => {
34763
- const [hovered, setHovered] = React__namespace.useState(false);
34764
- // This is more so required for the re-rendering that occurs when state
34765
- // updates; specifically with regard to line weighting and polygon focus.
34766
- const [focusVisible, setFocusVisible] = React__namespace.useState(false);
34767
+ const PolygonGraph = props => {
34767
34768
  const {
34768
34769
  dispatch
34769
34770
  } = props;
34770
34771
  const {
34772
+ numSides,
34771
34773
  coords,
34772
- showAngles,
34773
- showSides,
34774
- range,
34775
34774
  snapStep,
34776
34775
  snapTo = "grid"
34777
34776
  } = props.graphState;
34777
+ const graphConfig = useGraphConfig();
34778
+
34779
+ // Ref to implement the dragging behavior on a Limited/Closed Polygon.
34780
+ const polygonRef = React__namespace.useRef(null);
34781
+ // Ref to manage dynamic focus for the points in Unlimited Polygon
34782
+ const pointsRef = React__namespace.useRef([]);
34783
+ // Ref to manage the last move time for a Limited Polygon.
34784
+ const lastMoveTimeRef = React__namespace.useRef(0);
34785
+
34786
+ // Dimensions to build the graph overlay for Unlimited Polgyon.
34778
34787
  const {
34779
- disableKeyboardInteraction
34780
- } = useGraphConfig();
34788
+ range: [x, y]
34789
+ } = graphConfig;
34790
+ const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
34781
34791
 
34782
34792
  // TODO(benchristel): can the default set of points be removed here? I don't
34783
34793
  // think coords can be null.
34784
34794
  const points = coords ?? [[0, 0]];
34785
- const ref = React__namespace.useRef(null);
34795
+
34796
+ // Logic to build the dragging experience. Primarily used by Limited Polygon.
34786
34797
  const dragReferencePoint = points[0];
34787
34798
  const constrain = ["angles", "sides"].includes(snapTo) ? p => p : p => snap(snapStep, p);
34788
34799
  const {
34789
34800
  dragging
34790
34801
  } = useDraggable({
34791
- gestureTarget: ref,
34802
+ gestureTarget: polygonRef,
34792
34803
  point: dragReferencePoint,
34793
34804
  onMove: newPoint => {
34794
34805
  const delta = mafs.vec.sub(newPoint, dragReferencePoint);
@@ -34796,7 +34807,61 @@ const LimitedPolygonGraph = props => {
34796
34807
  },
34797
34808
  constrainKeyboardMovement: constrain
34798
34809
  });
34799
- const lastMoveTime = React__namespace.useRef(0);
34810
+
34811
+ // Hover/focus state for Limited Polgyon effects.
34812
+ const [hovered, setHovered] = React__namespace.useState(false);
34813
+ // This is more so required for the re-rendering that occurs when state
34814
+ // updates; specifically with regard to line weighting and polygon focus.
34815
+ const [focusVisible, setFocusVisible] = React__namespace.useState(false);
34816
+
34817
+ // This useEffect is to handle the focus snapping for Unlimited Polygon.
34818
+ React__namespace.useEffect(() => {
34819
+ const focusedIndex = props.graphState.focusedPointIndex;
34820
+ if (focusedIndex != null) {
34821
+ pointsRef.current[focusedIndex]?.focus();
34822
+ }
34823
+ }, [props.graphState.focusedPointIndex, pointsRef]);
34824
+ const statefulProps = {
34825
+ ...props,
34826
+ graphConfig,
34827
+ polygonRef,
34828
+ pointsRef,
34829
+ lastMoveTimeRef,
34830
+ left,
34831
+ top,
34832
+ dragging,
34833
+ points,
34834
+ constrain,
34835
+ hovered,
34836
+ setHovered,
34837
+ focusVisible,
34838
+ setFocusVisible
34839
+ };
34840
+ return numSides === "unlimited" ? UnlimitedPolygonGraph(statefulProps) : LimitedPolygonGraph(statefulProps);
34841
+ };
34842
+ const LimitedPolygonGraph = statefulProps => {
34843
+ const {
34844
+ dispatch,
34845
+ hovered,
34846
+ setHovered,
34847
+ focusVisible,
34848
+ setFocusVisible,
34849
+ graphConfig,
34850
+ polygonRef,
34851
+ lastMoveTimeRef,
34852
+ dragging,
34853
+ points,
34854
+ constrain
34855
+ } = statefulProps;
34856
+ const {
34857
+ showAngles,
34858
+ showSides,
34859
+ range,
34860
+ snapTo = "grid"
34861
+ } = statefulProps.graphState;
34862
+ const {
34863
+ disableKeyboardInteraction
34864
+ } = graphConfig;
34800
34865
  const lines = getLines(points);
34801
34866
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
34802
34867
  points: [...points],
@@ -34835,7 +34900,7 @@ const LimitedPolygonGraph = props => {
34835
34900
  points: [...points],
34836
34901
  color: "transparent",
34837
34902
  svgPolygonProps: {
34838
- ref,
34903
+ ref: polygonRef,
34839
34904
  tabIndex: disableKeyboardInteraction ? -1 : 0,
34840
34905
  strokeWidth: TARGET_SIZE,
34841
34906
  style: {
@@ -34847,13 +34912,13 @@ const LimitedPolygonGraph = props => {
34847
34912
  // Required to remove line weighting when user clicks away
34848
34913
  // from the focused polygon
34849
34914
  onKeyDownCapture: () => {
34850
- setFocusVisible(hasFocusVisible(ref.current));
34915
+ setFocusVisible(hasFocusVisible(polygonRef.current));
34851
34916
  },
34852
34917
  // Required for lines to darken on focus
34853
- onFocus: () => setFocusVisible(hasFocusVisible(ref.current)),
34918
+ onFocus: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
34854
34919
  // Required for line weighting to update on blur. Without this,
34855
34920
  // the user has to hover over the shape for it to update
34856
- onBlur: () => setFocusVisible(hasFocusVisible(ref.current)),
34921
+ onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
34857
34922
  className: "movable-polygon"
34858
34923
  }
34859
34924
  }), points.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
@@ -34865,94 +34930,86 @@ const LimitedPolygonGraph = props => {
34865
34930
  const now = Date.now();
34866
34931
  const targetFPS = 40;
34867
34932
  const moveThresholdTime = 1000 / targetFPS;
34868
- if (now - lastMoveTime.current > moveThresholdTime) {
34933
+ if (now - lastMoveTimeRef.current > moveThresholdTime) {
34869
34934
  dispatch(actions.polygon.movePoint(i, destination));
34870
- lastMoveTime.current = now;
34935
+ lastMoveTimeRef.current = now;
34871
34936
  }
34872
34937
  }
34873
34938
  })));
34874
34939
  };
34875
- const UnlimitedPolygonGraph = props => {
34940
+ const UnlimitedPolygonGraph = statefulProps => {
34876
34941
  const {
34877
- dispatch
34878
- } = props;
34942
+ dispatch,
34943
+ graphConfig,
34944
+ left,
34945
+ top,
34946
+ pointsRef,
34947
+ points
34948
+ } = statefulProps;
34879
34949
  const {
34880
34950
  coords,
34881
34951
  closedPolygon
34882
- } = props.graphState;
34883
- const graphConfig = useGraphConfig();
34884
- const {
34885
- range: [x, y],
34886
- graphDimensionsInPixels
34887
- } = graphConfig;
34888
- const widthPx = graphDimensionsInPixels[0];
34889
- const heightPx = graphDimensionsInPixels[1];
34890
- const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
34891
- const pointRef = React__namespace.useRef([]);
34952
+ } = statefulProps.graphState;
34892
34953
 
34893
- // TODO(benchristel): can the default set of points be removed here? I don't
34894
- // think coords can be null.
34895
- const points = coords ?? [[0, 0]];
34896
- React__namespace.useEffect(() => {
34897
- const focusedIndex = props.graphState.focusedPointIndex;
34898
- if (focusedIndex != null) {
34899
- pointRef.current[focusedIndex]?.focus();
34900
- }
34901
- }, [props.graphState.focusedPointIndex, pointRef]);
34954
+ // If the polygon is closed, return a LimitedPolygon component.
34902
34955
  if (closedPolygon) {
34903
34956
  const closedPolygonProps = {
34904
- ...props,
34957
+ ...statefulProps,
34905
34958
  numSides: coords.length
34906
34959
  };
34907
34960
  return /*#__PURE__*/React__namespace.createElement(LimitedPolygonGraph, closedPolygonProps);
34908
- } else {
34909
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
34961
+ }
34962
+ const {
34963
+ graphDimensionsInPixels
34964
+ } = graphConfig;
34965
+ const widthPx = graphDimensionsInPixels[0];
34966
+ const heightPx = graphDimensionsInPixels[1];
34967
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
34968
+ style: {
34969
+ fill: "rgba(0,0,0,0)",
34970
+ cursor: "crosshair"
34971
+ },
34972
+ width: widthPx,
34973
+ height: heightPx,
34974
+ x: left,
34975
+ y: top,
34976
+ onClick: event => {
34977
+ const elementRect = event.currentTarget.getBoundingClientRect();
34978
+ const x = event.clientX - elementRect.x;
34979
+ const y = event.clientY - elementRect.y;
34980
+ const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
34981
+ dispatch(actions.polygon.addPoint(graphCoordinates[0]));
34982
+ }
34983
+ }), /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
34984
+ points: [...points],
34985
+ color: "var(--movable-line-stroke-color)",
34986
+ svgPolylineProps: {
34987
+ strokeWidth: "var(--movable-line-stroke-weight)",
34910
34988
  style: {
34911
- fill: "rgba(0,0,0,0)",
34912
- cursor: "crosshair"
34913
- },
34914
- width: widthPx,
34915
- height: heightPx,
34916
- x: left,
34917
- y: top,
34918
- onClick: event => {
34919
- const elementRect = event.currentTarget.getBoundingClientRect();
34920
- const x = event.clientX - elementRect.x;
34921
- const y = event.clientY - elementRect.y;
34922
- const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
34923
- dispatch(actions.polygon.addPoint(graphCoordinates[0]));
34924
- }
34925
- }), /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
34926
- points: [...points],
34927
- color: "var(--movable-line-stroke-color)",
34928
- svgPolylineProps: {
34929
- strokeWidth: "var(--movable-line-stroke-weight)",
34930
- style: {
34931
- fill: "transparent"
34932
- }
34989
+ fill: "transparent"
34933
34990
  }
34934
- }), props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34935
- key: i,
34936
- point: point,
34937
- sequenceNumber: i + 1,
34938
- onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
34939
- ref: ref => {
34940
- pointRef.current[i] = ref;
34941
- },
34942
- onFocus: () => {
34943
- dispatch(actions.polygon.focusPoint(i));
34944
- },
34945
- onClick: () => {
34946
- // If the point being clicked is the first point and
34947
- // there's enough points to form a polygon (3 or more)
34948
- // Close the shape before setting focus.
34949
- if (i === 0 && props.graphState.coords.length >= 3) {
34950
- dispatch(actions.polygon.closePolygon());
34951
- }
34952
- dispatch(actions.polygon.clickPoint(i));
34991
+ }
34992
+ }), coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34993
+ key: i,
34994
+ point: point,
34995
+ sequenceNumber: i + 1,
34996
+ onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
34997
+ ref: ref => {
34998
+ pointsRef.current[i] = ref;
34999
+ },
35000
+ onFocus: () => {
35001
+ dispatch(actions.polygon.focusPoint(i));
35002
+ },
35003
+ onClick: () => {
35004
+ // If the point being clicked is the first point and
35005
+ // there's enough points to form a polygon (3 or more)
35006
+ // Close the shape before setting focus.
35007
+ if (i === 0 && coords.length >= 3) {
35008
+ dispatch(actions.polygon.closePolygon());
34953
35009
  }
34954
- })));
34955
- }
35010
+ dispatch(actions.polygon.clickPoint(i));
35011
+ }
35012
+ })));
34956
35013
  };
34957
35014
  function getLines(points) {
34958
35015
  return points.map((point, i) => {
@@ -34964,17 +35021,13 @@ const hasFocusVisible = element => {
34964
35021
  const matches = selector => element?.matches(selector) ?? false;
34965
35022
  try {
34966
35023
  return matches(":focus-visible");
34967
- } catch (e) {
35024
+ } catch {
34968
35025
  // jsdom doesn't support :focus-visible
34969
35026
  // (see https://github.com/jsdom/jsdom/issues/3426),
34970
35027
  // so the call to matches(":focus-visible") will fail in tests.
34971
35028
  return matches(":focus");
34972
35029
  }
34973
35030
  };
34974
- const PolygonGraph = props => {
34975
- const numSides = props.graphState.numSides;
34976
- return numSides === "unlimited" ? UnlimitedPolygonGraph(props) : LimitedPolygonGraph(props);
34977
- };
34978
35031
 
34979
35032
  function renderQuadraticGraph(state, dispatch) {
34980
35033
  return {
@@ -40395,7 +40448,7 @@ class Sortable extends React__namespace.Component {
40395
40448
  this.setState({
40396
40449
  items
40397
40450
  }, () => {
40398
- this.props.onMeasure && this.props.onMeasure({
40451
+ this.props.onMeasure?.({
40399
40452
  widths: widths,
40400
40453
  heights: heights
40401
40454
  });
@@ -40445,7 +40498,7 @@ class Sortable extends React__namespace.Component {
40445
40498
  this.setState({
40446
40499
  items: nextItems
40447
40500
  });
40448
- this.props.onChange && this.props.onChange({});
40501
+ this.props.onChange?.({});
40449
40502
  }
40450
40503
  onMouseMove(key) {
40451
40504
  // Dragging: Rearrange items based on draggable's position
@@ -40525,7 +40578,7 @@ class Sortable extends React__namespace.Component {
40525
40578
  // HACK: We need to know *that* the widget changed, but currently it's
40526
40579
  // not set up in a nice way to tell us *how* it changed, since the
40527
40580
  // permutation of the items is stored in state.
40528
- this.props.onChange && this.props.onChange({});
40581
+ this.props.onChange?.({});
40529
40582
  });
40530
40583
 
40531
40584
  // @ts-expect-error - TS2339 - Property 'animationFrameRequest' does not exist on type 'Sortable'.
@@ -43250,7 +43303,7 @@ class Card extends React__namespace.Component {
43250
43303
  dragging: true
43251
43304
  });
43252
43305
  this.bindMouseMoveUp();
43253
- this.props.onMouseDown && this.props.onMouseDown(loc, this);
43306
+ this.props.onMouseDown?.(loc, this);
43254
43307
  }
43255
43308
  };
43256
43309
  onMouseMove = event => {
@@ -43260,7 +43313,7 @@ class Card extends React__namespace.Component {
43260
43313
  event.preventDefault();
43261
43314
  const loc = Util.extractPointerLocation(event);
43262
43315
  if (loc) {
43263
- this.props.onMouseMove && this.props.onMouseMove(loc);
43316
+ this.props.onMouseMove?.(loc);
43264
43317
  }
43265
43318
  };
43266
43319
  onMouseUp = event => {
@@ -43271,7 +43324,7 @@ class Card extends React__namespace.Component {
43271
43324
  dragging: false
43272
43325
  });
43273
43326
  this.unbindMouseMoveUp();
43274
- this.props.onMouseUp && this.props.onMouseUp(loc);
43327
+ this.props.onMouseUp?.(loc);
43275
43328
  }
43276
43329
  };
43277
43330
  render() {
@@ -45477,7 +45530,7 @@ const init = function () {
45477
45530
 
45478
45531
  // This file is processed by a Rollup plugin (replace) to inject the production
45479
45532
  const libName = "@khanacademy/perseus";
45480
- const libVersion = "47.0.1";
45533
+ const libVersion = "48.0.0";
45481
45534
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45482
45535
 
45483
45536
  const apiVersion = {
@@ -46237,28 +46290,6 @@ class ServerItemRenderer extends React__namespace.Component {
46237
46290
  return this.questionRenderer.getUserInputMap();
46238
46291
  }
46239
46292
 
46240
- /**
46241
- * Grades the item.
46242
- *
46243
- * @deprecated use scorePerseusItem
46244
- */
46245
- scoreInput() {
46246
- const guess = this.getUserInput();
46247
- const score = scorePerseusItem(this.props.item.question, guess, this.context.strings, this.context.locale);
46248
-
46249
- // Continue to include an empty guess for the now defunct answer area.
46250
- // TODO(alex): Check whether we rely on the format here for
46251
- // analyzing ProblemLogs. If not, remove this layer.
46252
- const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
46253
- const keScore = keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
46254
- const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
46255
- this.setState({
46256
- questionCompleted: keScore.correct,
46257
- questionHighlightedWidgets: emptyQuestionAreaWidgets
46258
- });
46259
- return keScore;
46260
- }
46261
-
46262
46293
  /**
46263
46294
  * Returns an array of all widget IDs in the order they occur in
46264
46295
  * the question content.