@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/es/index.js CHANGED
@@ -2287,9 +2287,6 @@ function scoreExpression(userInput, rubric, strings, locale) {
2287
2287
  // We matched a graded answer form, so we can now tell the user
2288
2288
  // whether their input was correct or incorrect, and hand out
2289
2289
  // points accordingly
2290
- // TODO(eater): Seems silly to translate result to this
2291
- // invalid/points thing and immediately translate it back in
2292
- // ItemRenderer.scoreInput()
2293
2290
  return {
2294
2291
  type: "points",
2295
2292
  earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
@@ -3240,7 +3237,7 @@ const supportsPassiveEvents = () => {
3240
3237
  window.addEventListener("testPassive", null, opts);
3241
3238
  // @ts-expect-error - TS2769 - No overload matches this call.
3242
3239
  window.removeEventListener("testPassive", null, opts);
3243
- } catch (e) {
3240
+ } catch (_unused) {
3244
3241
  // Intentionally left empty!
3245
3242
  }
3246
3243
  return supportsPassive;
@@ -3483,7 +3480,9 @@ function changeViewportTag(contentString, callback) {
3483
3480
  // finished resetting.
3484
3481
  // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
3485
3482
  // eslint-disable-next-line no-restricted-syntax
3486
- callback && setTimeout(callback, 0);
3483
+ if (callback != null) {
3484
+ setTimeout(callback, 0);
3485
+ }
3487
3486
  }, 0);
3488
3487
  }
3489
3488
 
@@ -16768,7 +16767,7 @@ class ImageLoader extends React.Component {
16768
16767
  onKeyUp = e => {
16769
16768
  // 13 is enter key, 32 is space key
16770
16769
  if (e.keyCode === 13 || e.keyCode === 32) {
16771
- imgProps.onClick && imgProps.onClick(e);
16770
+ imgProps.onClick == null || imgProps.onClick(e);
16772
16771
  }
16773
16772
  };
16774
16773
  onKeyDown = e => {
@@ -16849,6 +16848,7 @@ const labelDataCache = {};
16849
16848
  // Write our own JSONP handler because all the other ones don't do things we
16850
16849
  // need.
16851
16850
  const doJSONP = function doJSONP(url, options) {
16851
+ var _document$head2;
16852
16852
  options = _extends({
16853
16853
  callbackName: "callback",
16854
16854
  success: $.noop,
@@ -16862,7 +16862,8 @@ const doJSONP = function doJSONP(url, options) {
16862
16862
 
16863
16863
  // A cleanup function to run when we're done.
16864
16864
  function cleanup() {
16865
- document.head && document.head.removeChild(script);
16865
+ var _document$head;
16866
+ (_document$head = document.head) == null || _document$head.removeChild(script);
16866
16867
  delete window[options.callbackName];
16867
16868
  }
16868
16869
 
@@ -16880,7 +16881,7 @@ const doJSONP = function doJSONP(url, options) {
16880
16881
  });
16881
16882
 
16882
16883
  // Insert the script to start the download.
16883
- document.head && document.head.appendChild(script);
16884
+ (_document$head2 = document.head) == null || _document$head2.appendChild(script);
16884
16885
  };
16885
16886
 
16886
16887
  // For offline exercises in the mobile app, we download the graphie data
@@ -17050,6 +17051,7 @@ class SvgImage extends React.Component {
17050
17051
  });
17051
17052
  };
17052
17053
  this._handleZoomClick = e => {
17054
+ var _this$props$trackInte, _this$props;
17053
17055
  const $image = $(e.target);
17054
17056
 
17055
17057
  // It's possible that the image is already displayed at its
@@ -17066,7 +17068,7 @@ class SvgImage extends React.Component {
17066
17068
  $image.width() < this.props.width || this.props.zoomToFullSizeOnMobile) {
17067
17069
  ZoomService.handleZoomClick(e, this.props.zoomToFullSizeOnMobile);
17068
17070
  }
17069
- this.props.trackInteraction && this.props.trackInteraction();
17071
+ (_this$props$trackInte = (_this$props = this.props).trackInteraction) == null || _this$props$trackInte.call(_this$props);
17070
17072
  };
17071
17073
  this.handleUpdate = status => {
17072
17074
  this.props.onUpdate();
@@ -19602,8 +19604,8 @@ class WidgetContainer extends React.Component {
19602
19604
  });
19603
19605
 
19604
19606
  // Note: if you add more props here, please consider whether or not
19605
- // it should be auto-serialized (e.g. used in scoreInput()). See
19606
- // widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
19607
+ // it should be auto-serialized.
19608
+ // See widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
19607
19609
 
19608
19610
  // We default to an empty object for style instead of null
19609
19611
  // because of a strange bug where the static styles aren't applied
@@ -20582,14 +20584,6 @@ class Renderer extends React.Component {
20582
20584
  this.getWidgetIds = () => {
20583
20585
  return this.widgetIds;
20584
20586
  };
20585
- /**
20586
- * @deprecated use scorePerseusItem
20587
- */
20588
- this.guessAndScore = () => {
20589
- const totalGuess = this.getUserInput();
20590
- const totalScore = this.score();
20591
- return [totalGuess, totalScore];
20592
- };
20593
20587
  // TranslationLinter callback
20594
20588
  this.handletranslationLintErrors = lintErrors => {
20595
20589
  if (!this._isMounted) {
@@ -21556,11 +21550,11 @@ class SimpleKeypadInput extends React.Component {
21556
21550
  keypadType: KeypadType.FRACTION
21557
21551
  }, () => {
21558
21552
  if (_this._isMounted) {
21559
- onFocus && onFocus();
21553
+ onFocus == null || onFocus();
21560
21554
  }
21561
21555
  });
21562
21556
  } else {
21563
- onFocus && onFocus();
21557
+ onFocus == null || onFocus();
21564
21558
  }
21565
21559
  },
21566
21560
  value: value == null ? "" : "" + value
@@ -21799,9 +21793,6 @@ function scoreInputNumber(userInput, rubric, strings) {
21799
21793
  // If `currentValue` is not TeX, this should be a no-op.
21800
21794
  const currentValue = ParseTex$1(userInput.currentValue);
21801
21795
  const result = val(currentValue);
21802
-
21803
- // TODO(eater): Seems silly to translate result to this invalid/points
21804
- // thing and immediately translate it back in ItemRenderer.scoreInput()
21805
21796
  if (result.empty) {
21806
21797
  return {
21807
21798
  type: "invalid",
@@ -22168,10 +22159,6 @@ function scoreNumericInput(userInput, rubric, strings) {
22168
22159
  message: (_matchedAnswer$messag = matchedAnswer == null ? void 0 : matchedAnswer.message) != null ? _matchedAnswer$messag : null,
22169
22160
  guess: localValue
22170
22161
  };
22171
-
22172
- // TODO(eater): Seems silly to translate result to this
22173
- // invalid/points thing and immediately translate it
22174
- // back in ItemRenderer.scoreInput()
22175
22162
  if (result.empty) {
22176
22163
  return {
22177
22164
  type: "invalid",
@@ -25024,7 +25011,7 @@ const scrollElementToOffset = (elementOrWindow, offset, duration = 0, callback)
25024
25011
  // TODO(jeff, WEB-1378): Use Wonder Blocks Timing API
25025
25012
  // eslint-disable-next-line no-restricted-syntax
25026
25013
  clearInterval(scrollInterval);
25027
- callback && callback();
25014
+ callback == null || callback();
25028
25015
  }
25029
25016
  }, tickLength);
25030
25017
  };
@@ -27048,7 +27035,7 @@ class CSProgram extends React.Component {
27048
27035
  let data = {};
27049
27036
  try {
27050
27037
  data = JSON.parse(e.originalEvent.data);
27051
- } catch (err) {
27038
+ } catch (_unused) {
27052
27039
  return;
27053
27040
  }
27054
27041
  if (_.isUndefined(data.testsPassed)) {
@@ -28104,7 +28091,6 @@ class GradedGroupSet extends React.Component {
28104
28091
  };
28105
28092
  }
28106
28093
  shouldComponentUpdate(nextProps, nextState) {
28107
- nextProps.gradedGroups;
28108
28094
  return nextProps !== this.props || nextState !== this.state;
28109
28095
  }
28110
28096
  getPromptJSON() {
@@ -29573,7 +29559,7 @@ class Iframe extends React.Component {
29573
29559
  let data = {};
29574
29560
  try {
29575
29561
  data = JSON.parse(e.originalEvent.data);
29576
- } catch (err) {
29562
+ } catch (_unused) {
29577
29563
  return;
29578
29564
  }
29579
29565
  if (_.isUndefined(data.testsPassed)) {
@@ -30638,10 +30624,12 @@ class Graph extends React.Component {
30638
30624
  this.props.onMouseDown(coord);
30639
30625
  }, this) : undefined;
30640
30626
  const onMouseOver = $instructionsWrapper ? function () {
30641
- $instructionsWrapper && $instructionsWrapper.css("opacity", invisible);
30627
+ var _$instructionsWrapper;
30628
+ (_$instructionsWrapper = $instructionsWrapper) == null || _$instructionsWrapper.css("opacity", invisible);
30642
30629
  } : undefined;
30643
30630
  const onMouseOut = $instructionsWrapper ? function () {
30644
- $instructionsWrapper && $instructionsWrapper.css("opacity", visible);
30631
+ var _$instructionsWrapper2;
30632
+ (_$instructionsWrapper2 = $instructionsWrapper) == null || _$instructionsWrapper2.css("opacity", visible);
30645
30633
  } : undefined;
30646
30634
  /* eslint-enable indent */
30647
30635
 
@@ -31373,6 +31361,7 @@ function replaceOutsideTeX(mathString) {
31373
31361
  const parsed = mathOnlyParser(mathString);
31374
31362
  let result = "";
31375
31363
  for (const piece of parsed) {
31364
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
31376
31365
  piece.type === "math" ? result += "$" + piece.content + "$" : piece.type === "specialCharacter" ? result += escapeIfUnescaped(piece.content) : result += piece.content;
31377
31366
  }
31378
31367
  return `\\text{${result}}`;
@@ -34323,43 +34312,64 @@ function renderPointGraph(state, dispatch) {
34323
34312
  };
34324
34313
  }
34325
34314
  function PointGraph(props) {
34326
- const numPoints = props.graphState.numPoints;
34315
+ const {
34316
+ numPoints
34317
+ } = props.graphState;
34318
+ const graphConfig = useGraphConfig();
34319
+ const pointsRef = React.useRef([]);
34320
+
34321
+ // Dimensions to build the graph overlay for Unlimited Point.
34322
+ const {
34323
+ range: [x, y]
34324
+ } = graphConfig;
34325
+ const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
34326
+
34327
+ // This useEffect is to handle the focus snapping for Unlimited Point.
34328
+ React.useEffect(() => {
34329
+ const focusedIndex = props.graphState.focusedPointIndex;
34330
+ if (focusedIndex != null) {
34331
+ var _pointsRef$current$fo;
34332
+ (_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
34333
+ }
34334
+ }, [props.graphState.focusedPointIndex, pointsRef]);
34335
+ const statefulProps = _extends({}, props, {
34336
+ graphConfig,
34337
+ pointsRef,
34338
+ top,
34339
+ left
34340
+ });
34327
34341
  if (numPoints === "unlimited") {
34328
- return UnlimitedPointGraph(props);
34342
+ return UnlimitedPointGraph(statefulProps);
34329
34343
  }
34330
- return LimitedPointGraph(props);
34344
+ return LimitedPointGraph(statefulProps);
34331
34345
  }
34332
- function LimitedPointGraph(props) {
34346
+ function LimitedPointGraph(statefulProps) {
34333
34347
  const {
34334
34348
  dispatch
34335
- } = props;
34336
- return /*#__PURE__*/React.createElement(React.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
34349
+ } = statefulProps;
34350
+ return /*#__PURE__*/React.createElement(React.Fragment, null, statefulProps.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
34337
34351
  key: i,
34338
34352
  point: point,
34339
34353
  sequenceNumber: i + 1,
34340
34354
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
34341
34355
  })));
34342
34356
  }
34343
- function UnlimitedPointGraph(props) {
34357
+ function UnlimitedPointGraph(statefulProps) {
34344
34358
  const {
34345
- dispatch
34346
- } = props;
34347
- const graphConfig = useGraphConfig();
34359
+ dispatch,
34360
+ graphConfig,
34361
+ pointsRef,
34362
+ top,
34363
+ left
34364
+ } = statefulProps;
34365
+ const {
34366
+ coords
34367
+ } = statefulProps.graphState;
34348
34368
  const {
34349
- range: [[minX, maxX], [minY, maxY]]
34369
+ graphDimensionsInPixels
34350
34370
  } = graphConfig;
34351
- const width = maxX - minX;
34352
- const height = maxY - minY;
34353
- const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
34354
- const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
34355
- const itemsRef = React.useRef([]);
34356
- React.useEffect(() => {
34357
- const focusedIndex = props.graphState.focusedPointIndex;
34358
- if (focusedIndex != null) {
34359
- var _itemsRef$current$foc;
34360
- (_itemsRef$current$foc = itemsRef.current[focusedIndex]) == null || _itemsRef$current$foc.focus();
34361
- }
34362
- }, [props.graphState.focusedPointIndex, itemsRef]);
34371
+ const widthPx = graphDimensionsInPixels[0];
34372
+ const heightPx = graphDimensionsInPixels[1];
34363
34373
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
34364
34374
  style: {
34365
34375
  fill: "rgba(0,0,0,0)",
@@ -34376,13 +34386,13 @@ function UnlimitedPointGraph(props) {
34376
34386
  const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
34377
34387
  dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
34378
34388
  }
34379
- }), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
34389
+ }), coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
34380
34390
  key: i,
34381
34391
  point: point,
34382
34392
  sequenceNumber: i + 1,
34383
34393
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
34384
34394
  ref: ref => {
34385
- itemsRef.current[i] = ref;
34395
+ pointsRef.current[i] = ref;
34386
34396
  },
34387
34397
  onFocus: () => {
34388
34398
  dispatch(actions.pointGraph.focusPoint(i));
@@ -34429,36 +34439,42 @@ function renderPolygonGraph(state, dispatch) {
34429
34439
  interactiveElementsDescription: null
34430
34440
  };
34431
34441
  }
34432
- const LimitedPolygonGraph = props => {
34433
- const [hovered, setHovered] = React.useState(false);
34434
- // This is more so required for the re-rendering that occurs when state
34435
- // updates; specifically with regard to line weighting and polygon focus.
34436
- const [focusVisible, setFocusVisible] = React.useState(false);
34442
+ const PolygonGraph = props => {
34437
34443
  const {
34438
34444
  dispatch
34439
34445
  } = props;
34440
34446
  const {
34447
+ numSides,
34441
34448
  coords,
34442
- showAngles,
34443
- showSides,
34444
- range,
34445
34449
  snapStep,
34446
34450
  snapTo = "grid"
34447
34451
  } = props.graphState;
34452
+ const graphConfig = useGraphConfig();
34453
+
34454
+ // Ref to implement the dragging behavior on a Limited/Closed Polygon.
34455
+ const polygonRef = React.useRef(null);
34456
+ // Ref to manage dynamic focus for the points in Unlimited Polygon
34457
+ const pointsRef = React.useRef([]);
34458
+ // Ref to manage the last move time for a Limited Polygon.
34459
+ const lastMoveTimeRef = React.useRef(0);
34460
+
34461
+ // Dimensions to build the graph overlay for Unlimited Polgyon.
34448
34462
  const {
34449
- disableKeyboardInteraction
34450
- } = useGraphConfig();
34463
+ range: [x, y]
34464
+ } = graphConfig;
34465
+ const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
34451
34466
 
34452
34467
  // TODO(benchristel): can the default set of points be removed here? I don't
34453
34468
  // think coords can be null.
34454
34469
  const points = coords != null ? coords : [[0, 0]];
34455
- const ref = React.useRef(null);
34470
+
34471
+ // Logic to build the dragging experience. Primarily used by Limited Polygon.
34456
34472
  const dragReferencePoint = points[0];
34457
34473
  const constrain = ["angles", "sides"].includes(snapTo) ? p => p : p => snap(snapStep, p);
34458
34474
  const {
34459
34475
  dragging
34460
34476
  } = useDraggable({
34461
- gestureTarget: ref,
34477
+ gestureTarget: polygonRef,
34462
34478
  point: dragReferencePoint,
34463
34479
  onMove: newPoint => {
34464
34480
  const delta = vec.sub(newPoint, dragReferencePoint);
@@ -34466,7 +34482,61 @@ const LimitedPolygonGraph = props => {
34466
34482
  },
34467
34483
  constrainKeyboardMovement: constrain
34468
34484
  });
34469
- const lastMoveTime = React.useRef(0);
34485
+
34486
+ // Hover/focus state for Limited Polgyon effects.
34487
+ const [hovered, setHovered] = React.useState(false);
34488
+ // This is more so required for the re-rendering that occurs when state
34489
+ // updates; specifically with regard to line weighting and polygon focus.
34490
+ const [focusVisible, setFocusVisible] = React.useState(false);
34491
+
34492
+ // This useEffect is to handle the focus snapping for Unlimited Polygon.
34493
+ React.useEffect(() => {
34494
+ const focusedIndex = props.graphState.focusedPointIndex;
34495
+ if (focusedIndex != null) {
34496
+ var _pointsRef$current$fo;
34497
+ (_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
34498
+ }
34499
+ }, [props.graphState.focusedPointIndex, pointsRef]);
34500
+ const statefulProps = _extends({}, props, {
34501
+ graphConfig,
34502
+ polygonRef,
34503
+ pointsRef,
34504
+ lastMoveTimeRef,
34505
+ left,
34506
+ top,
34507
+ dragging,
34508
+ points,
34509
+ constrain,
34510
+ hovered,
34511
+ setHovered,
34512
+ focusVisible,
34513
+ setFocusVisible
34514
+ });
34515
+ return numSides === "unlimited" ? UnlimitedPolygonGraph(statefulProps) : LimitedPolygonGraph(statefulProps);
34516
+ };
34517
+ const LimitedPolygonGraph = statefulProps => {
34518
+ const {
34519
+ dispatch,
34520
+ hovered,
34521
+ setHovered,
34522
+ focusVisible,
34523
+ setFocusVisible,
34524
+ graphConfig,
34525
+ polygonRef,
34526
+ lastMoveTimeRef,
34527
+ dragging,
34528
+ points,
34529
+ constrain
34530
+ } = statefulProps;
34531
+ const {
34532
+ showAngles,
34533
+ showSides,
34534
+ range,
34535
+ snapTo = "grid"
34536
+ } = statefulProps.graphState;
34537
+ const {
34538
+ disableKeyboardInteraction
34539
+ } = graphConfig;
34470
34540
  const lines = getLines(points);
34471
34541
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Polygon, {
34472
34542
  points: [...points],
@@ -34504,7 +34574,7 @@ const LimitedPolygonGraph = props => {
34504
34574
  points: [...points],
34505
34575
  color: "transparent",
34506
34576
  svgPolygonProps: {
34507
- ref,
34577
+ ref: polygonRef,
34508
34578
  tabIndex: disableKeyboardInteraction ? -1 : 0,
34509
34579
  strokeWidth: TARGET_SIZE,
34510
34580
  style: {
@@ -34516,13 +34586,13 @@ const LimitedPolygonGraph = props => {
34516
34586
  // Required to remove line weighting when user clicks away
34517
34587
  // from the focused polygon
34518
34588
  onKeyDownCapture: () => {
34519
- setFocusVisible(hasFocusVisible(ref.current));
34589
+ setFocusVisible(hasFocusVisible(polygonRef.current));
34520
34590
  },
34521
34591
  // Required for lines to darken on focus
34522
- onFocus: () => setFocusVisible(hasFocusVisible(ref.current)),
34592
+ onFocus: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
34523
34593
  // Required for line weighting to update on blur. Without this,
34524
34594
  // the user has to hover over the shape for it to update
34525
- onBlur: () => setFocusVisible(hasFocusVisible(ref.current)),
34595
+ onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
34526
34596
  className: "movable-polygon"
34527
34597
  }
34528
34598
  }), points.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
@@ -34534,94 +34604,85 @@ const LimitedPolygonGraph = props => {
34534
34604
  const now = Date.now();
34535
34605
  const targetFPS = 40;
34536
34606
  const moveThresholdTime = 1000 / targetFPS;
34537
- if (now - lastMoveTime.current > moveThresholdTime) {
34607
+ if (now - lastMoveTimeRef.current > moveThresholdTime) {
34538
34608
  dispatch(actions.polygon.movePoint(i, destination));
34539
- lastMoveTime.current = now;
34609
+ lastMoveTimeRef.current = now;
34540
34610
  }
34541
34611
  }
34542
34612
  })));
34543
34613
  };
34544
- const UnlimitedPolygonGraph = props => {
34614
+ const UnlimitedPolygonGraph = statefulProps => {
34545
34615
  const {
34546
- dispatch
34547
- } = props;
34616
+ dispatch,
34617
+ graphConfig,
34618
+ left,
34619
+ top,
34620
+ pointsRef,
34621
+ points
34622
+ } = statefulProps;
34548
34623
  const {
34549
34624
  coords,
34550
34625
  closedPolygon
34551
- } = props.graphState;
34552
- const graphConfig = useGraphConfig();
34626
+ } = statefulProps.graphState;
34627
+
34628
+ // If the polygon is closed, return a LimitedPolygon component.
34629
+ if (closedPolygon) {
34630
+ const closedPolygonProps = _extends({}, statefulProps, {
34631
+ numSides: coords.length
34632
+ });
34633
+ return /*#__PURE__*/React.createElement(LimitedPolygonGraph, closedPolygonProps);
34634
+ }
34553
34635
  const {
34554
- range: [x, y],
34555
34636
  graphDimensionsInPixels
34556
34637
  } = graphConfig;
34557
34638
  const widthPx = graphDimensionsInPixels[0];
34558
34639
  const heightPx = graphDimensionsInPixels[1];
34559
- const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
34560
- const pointRef = React.useRef([]);
34561
-
34562
- // TODO(benchristel): can the default set of points be removed here? I don't
34563
- // think coords can be null.
34564
- const points = coords != null ? coords : [[0, 0]];
34565
- React.useEffect(() => {
34566
- const focusedIndex = props.graphState.focusedPointIndex;
34567
- if (focusedIndex != null) {
34568
- var _pointRef$current$foc;
34569
- (_pointRef$current$foc = pointRef.current[focusedIndex]) == null || _pointRef$current$foc.focus();
34640
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
34641
+ style: {
34642
+ fill: "rgba(0,0,0,0)",
34643
+ cursor: "crosshair"
34644
+ },
34645
+ width: widthPx,
34646
+ height: heightPx,
34647
+ x: left,
34648
+ y: top,
34649
+ onClick: event => {
34650
+ const elementRect = event.currentTarget.getBoundingClientRect();
34651
+ const x = event.clientX - elementRect.x;
34652
+ const y = event.clientY - elementRect.y;
34653
+ const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
34654
+ dispatch(actions.polygon.addPoint(graphCoordinates[0]));
34570
34655
  }
34571
- }, [props.graphState.focusedPointIndex, pointRef]);
34572
- if (closedPolygon) {
34573
- const closedPolygonProps = _extends({}, props, {
34574
- numSides: coords.length
34575
- });
34576
- return /*#__PURE__*/React.createElement(LimitedPolygonGraph, closedPolygonProps);
34577
- } else {
34578
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
34656
+ }), /*#__PURE__*/React.createElement(Polyline, {
34657
+ points: [...points],
34658
+ color: "var(--movable-line-stroke-color)",
34659
+ svgPolylineProps: {
34660
+ strokeWidth: "var(--movable-line-stroke-weight)",
34579
34661
  style: {
34580
- fill: "rgba(0,0,0,0)",
34581
- cursor: "crosshair"
34582
- },
34583
- width: widthPx,
34584
- height: heightPx,
34585
- x: left,
34586
- y: top,
34587
- onClick: event => {
34588
- const elementRect = event.currentTarget.getBoundingClientRect();
34589
- const x = event.clientX - elementRect.x;
34590
- const y = event.clientY - elementRect.y;
34591
- const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
34592
- dispatch(actions.polygon.addPoint(graphCoordinates[0]));
34593
- }
34594
- }), /*#__PURE__*/React.createElement(Polyline, {
34595
- points: [...points],
34596
- color: "var(--movable-line-stroke-color)",
34597
- svgPolylineProps: {
34598
- strokeWidth: "var(--movable-line-stroke-weight)",
34599
- style: {
34600
- fill: "transparent"
34601
- }
34662
+ fill: "transparent"
34602
34663
  }
34603
- }), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
34604
- key: i,
34605
- point: point,
34606
- sequenceNumber: i + 1,
34607
- onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
34608
- ref: ref => {
34609
- pointRef.current[i] = ref;
34610
- },
34611
- onFocus: () => {
34612
- dispatch(actions.polygon.focusPoint(i));
34613
- },
34614
- onClick: () => {
34615
- // If the point being clicked is the first point and
34616
- // there's enough points to form a polygon (3 or more)
34617
- // Close the shape before setting focus.
34618
- if (i === 0 && props.graphState.coords.length >= 3) {
34619
- dispatch(actions.polygon.closePolygon());
34620
- }
34621
- dispatch(actions.polygon.clickPoint(i));
34664
+ }
34665
+ }), coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
34666
+ key: i,
34667
+ point: point,
34668
+ sequenceNumber: i + 1,
34669
+ onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
34670
+ ref: ref => {
34671
+ pointsRef.current[i] = ref;
34672
+ },
34673
+ onFocus: () => {
34674
+ dispatch(actions.polygon.focusPoint(i));
34675
+ },
34676
+ onClick: () => {
34677
+ // If the point being clicked is the first point and
34678
+ // there's enough points to form a polygon (3 or more)
34679
+ // Close the shape before setting focus.
34680
+ if (i === 0 && coords.length >= 3) {
34681
+ dispatch(actions.polygon.closePolygon());
34622
34682
  }
34623
- })));
34624
- }
34683
+ dispatch(actions.polygon.clickPoint(i));
34684
+ }
34685
+ })));
34625
34686
  };
34626
34687
  function getLines(points) {
34627
34688
  return points.map((point, i) => {
@@ -34636,17 +34697,13 @@ const hasFocusVisible = element => {
34636
34697
  };
34637
34698
  try {
34638
34699
  return matches(":focus-visible");
34639
- } catch (e) {
34700
+ } catch (_unused) {
34640
34701
  // jsdom doesn't support :focus-visible
34641
34702
  // (see https://github.com/jsdom/jsdom/issues/3426),
34642
34703
  // so the call to matches(":focus-visible") will fail in tests.
34643
34704
  return matches(":focus");
34644
34705
  }
34645
34706
  };
34646
- const PolygonGraph = props => {
34647
- const numSides = props.graphState.numSides;
34648
- return numSides === "unlimited" ? UnlimitedPolygonGraph(props) : LimitedPolygonGraph(props);
34649
- };
34650
34707
 
34651
34708
  function renderQuadraticGraph(state, dispatch) {
34652
34709
  return {
@@ -40012,7 +40069,8 @@ class Sortable extends React.Component {
40012
40069
  this.setState({
40013
40070
  items
40014
40071
  }, () => {
40015
- this.props.onMeasure && this.props.onMeasure({
40072
+ var _this$props$onMeasure, _this$props;
40073
+ (_this$props$onMeasure = (_this$props = this.props).onMeasure) == null || _this$props$onMeasure.call(_this$props, {
40016
40074
  widths: widths,
40017
40075
  heights: heights
40018
40076
  });
@@ -40038,6 +40096,7 @@ class Sortable extends React.Component {
40038
40096
  // for testing and other cases where dragging and dropping may
40039
40097
  // not be available
40040
40098
  moveOptionToIndex(option, index) {
40099
+ var _this$props$onChange, _this$props2;
40041
40100
  const {
40042
40101
  items
40043
40102
  } = this.state;
@@ -40062,7 +40121,7 @@ class Sortable extends React.Component {
40062
40121
  this.setState({
40063
40122
  items: nextItems
40064
40123
  });
40065
- this.props.onChange && this.props.onChange({});
40124
+ (_this$props$onChange = (_this$props2 = this.props).onChange) == null || _this$props$onChange.call(_this$props2, {});
40066
40125
  }
40067
40126
  onMouseMove(key) {
40068
40127
  // Dragging: Rearrange items based on draggable's position
@@ -40120,6 +40179,7 @@ class Sortable extends React.Component {
40120
40179
  // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
40121
40180
  // eslint-disable-next-line no-restricted-syntax
40122
40181
  const nextAnimationFrame = requestAnimationFrame(() => {
40182
+ var _this$props$onChange2, _this$props3;
40123
40183
  const items = _.map(this.state.items, function (item) {
40124
40184
  if (item.key === key) {
40125
40185
  item.state = ItemState.ANIMATING;
@@ -40142,7 +40202,7 @@ class Sortable extends React.Component {
40142
40202
  // HACK: We need to know *that* the widget changed, but currently it's
40143
40203
  // not set up in a nice way to tell us *how* it changed, since the
40144
40204
  // permutation of the items is stored in state.
40145
- this.props.onChange && this.props.onChange({});
40205
+ (_this$props$onChange2 = (_this$props3 = this.props).onChange) == null || _this$props$onChange2.call(_this$props3, {});
40146
40206
  });
40147
40207
 
40148
40208
  // @ts-expect-error - TS2339 - Property 'animationFrameRequest' does not exist on type 'Sortable'.
@@ -42820,11 +42880,12 @@ class Card extends React.Component {
42820
42880
  event.preventDefault();
42821
42881
  const loc = Util.extractPointerLocation(event);
42822
42882
  if (loc) {
42883
+ var _this$props$onMouseDo, _this$props;
42823
42884
  this.setState({
42824
42885
  dragging: true
42825
42886
  });
42826
42887
  this.bindMouseMoveUp();
42827
- this.props.onMouseDown && this.props.onMouseDown(loc, this);
42888
+ (_this$props$onMouseDo = (_this$props = this.props).onMouseDown) == null || _this$props$onMouseDo.call(_this$props, loc, this);
42828
42889
  }
42829
42890
  };
42830
42891
  this.onMouseMove = event => {
@@ -42834,18 +42895,20 @@ class Card extends React.Component {
42834
42895
  event.preventDefault();
42835
42896
  const loc = Util.extractPointerLocation(event);
42836
42897
  if (loc) {
42837
- this.props.onMouseMove && this.props.onMouseMove(loc);
42898
+ var _this$props$onMouseMo, _this$props2;
42899
+ (_this$props$onMouseMo = (_this$props2 = this.props).onMouseMove) == null || _this$props$onMouseMo.call(_this$props2, loc);
42838
42900
  }
42839
42901
  };
42840
42902
  this.onMouseUp = event => {
42841
42903
  event.preventDefault();
42842
42904
  const loc = Util.extractPointerLocation(event);
42843
42905
  if (loc) {
42906
+ var _this$props$onMouseUp, _this$props3;
42844
42907
  this.setState({
42845
42908
  dragging: false
42846
42909
  });
42847
42910
  this.unbindMouseMoveUp();
42848
- this.props.onMouseUp && this.props.onMouseUp(loc);
42911
+ (_this$props$onMouseUp = (_this$props3 = this.props).onMouseUp) == null || _this$props$onMouseUp.call(_this$props3, loc);
42849
42912
  }
42850
42913
  };
42851
42914
  }
@@ -45107,7 +45170,7 @@ const init = function init() {
45107
45170
 
45108
45171
  // This file is processed by a Rollup plugin (replace) to inject the production
45109
45172
  const libName = "@khanacademy/perseus";
45110
- const libVersion = "47.0.1";
45173
+ const libVersion = "48.0.0";
45111
45174
  addLibraryVersionToPerseusDebug(libName, libVersion);
45112
45175
 
45113
45176
  const apiVersion = {
@@ -45869,28 +45932,6 @@ class ServerItemRenderer extends React.Component {
45869
45932
  return this.questionRenderer.getUserInputMap();
45870
45933
  }
45871
45934
 
45872
- /**
45873
- * Grades the item.
45874
- *
45875
- * @deprecated use scorePerseusItem
45876
- */
45877
- scoreInput() {
45878
- const guess = this.getUserInput();
45879
- const score = scorePerseusItem(this.props.item.question, guess, this.context.strings, this.context.locale);
45880
-
45881
- // Continue to include an empty guess for the now defunct answer area.
45882
- // TODO(alex): Check whether we rely on the format here for
45883
- // analyzing ProblemLogs. If not, remove this layer.
45884
- const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
45885
- const keScore = keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
45886
- const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
45887
- this.setState({
45888
- questionCompleted: keScore.correct,
45889
- questionHighlightedWidgets: emptyQuestionAreaWidgets
45890
- });
45891
- return keScore;
45892
- }
45893
-
45894
45935
  /**
45895
45936
  * Returns an array of all widget IDs in the order they occur in
45896
45937
  * the question content.