@khanacademy/perseus 33.2.1 → 33.3.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
@@ -23977,9 +23977,8 @@ const BaseRadio = function (props) {
23977
23977
  // element) We have to add some extra behavior to make
23978
23978
  // sure that we can still check the choice.
23979
23979
  let listElem = null;
23980
- let clickHandler = null;
23980
+ let clickHandler;
23981
23981
  if (editMode) {
23982
- // @ts-expect-error - TS2322 - Type '(e: any) => void' is not assignable to type 'null'.
23983
23982
  clickHandler = e => {
23984
23983
  // Traverse the parent nodes of the clicked
23985
23984
  // element.
@@ -24006,19 +24005,11 @@ const BaseRadio = function (props) {
24006
24005
  // somehow break something happening inside a choice's
24007
24006
  // child Renderers, by changing when we mount/unmount?
24008
24007
  return /*#__PURE__*/React__namespace.createElement("li", {
24009
- key: i
24010
- // @ts-expect-error - TS2322 - Type 'HTMLLIElement | null' is not assignable to type 'null'.
24011
- ,
24012
- ref: e => listElem = e
24013
- // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'string'.
24014
- ,
24015
- className: className
24016
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'MouseEventHandler<HTMLLIElement> | undefined'.
24017
- ,
24018
- onClick: clickHandler
24019
- // @ts-expect-error - TS2322 - Type '((e: TouchEvent) => void) | null' is not assignable to type 'TouchEventHandler<HTMLLIElement> | undefined'.
24020
- ,
24021
- onTouchStart: !labelWrap ? null : captureScratchpadTouchStart$1
24008
+ key: i,
24009
+ ref: e => listElem = e,
24010
+ className: className,
24011
+ onClick: clickHandler,
24012
+ onTouchStart: labelWrap ? undefined : captureScratchpadTouchStart$1
24022
24013
  }, /*#__PURE__*/React__namespace.createElement(Element, _extends({}, elementProps, {
24023
24014
  ref: ref
24024
24015
  })));
@@ -29718,9 +29709,7 @@ class NumberInput extends React__namespace.Component {
29718
29709
  onFocus: this._handleFocus,
29719
29710
  onBlur: this._handleBlur,
29720
29711
  onKeyPress: this._handleBlur,
29721
- onKeyDown: this._onKeyDown
29722
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
29723
- ,
29712
+ onKeyDown: this._onKeyDown,
29724
29713
  onTouchStart: captureScratchpadTouchStart,
29725
29714
  defaultValue: toNumericString(this.props.value, this.state.format),
29726
29715
  value: undefined
@@ -30359,7 +30348,8 @@ const actions = {
30359
30348
  addPoint,
30360
30349
  removePoint,
30361
30350
  focusPoint,
30362
- blurPoint
30351
+ blurPoint,
30352
+ clickPoint
30363
30353
  },
30364
30354
  polygon: {
30365
30355
  movePoint,
@@ -30421,6 +30411,13 @@ function blurPoint() {
30421
30411
  type: BLUR_POINT
30422
30412
  };
30423
30413
  }
30414
+ const CLICK_POINT = "click-point";
30415
+ function clickPoint(index) {
30416
+ return {
30417
+ type: CLICK_POINT,
30418
+ index
30419
+ };
30420
+ }
30424
30421
  const MOVE_ALL = "move-all";
30425
30422
  function moveAll(delta) {
30426
30423
  return {
@@ -30749,10 +30746,14 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
30749
30746
  },
30750
30747
  "data-testid": "movable-point",
30751
30748
  tabIndex: disableKeyboardInteraction ? -1 : tabIndex(focusBehavior),
30752
- onFocus: () => {
30753
- return getOnFocusChangeCallback(focusBehavior)(true);
30749
+ onFocus: event => {
30750
+ const callback = getOnFocusChangeCallback(focusBehavior);
30751
+ callback && callback(event, true);
30752
+ },
30753
+ onBlur: event => {
30754
+ const callback = getOnFocusChangeCallback(focusBehavior);
30755
+ callback && callback(event, false);
30754
30756
  },
30755
- onBlur: () => getOnFocusChangeCallback(focusBehavior)(false),
30756
30757
  onClick: onClick
30757
30758
  }, /*#__PURE__*/React__namespace.createElement("circle", {
30758
30759
  className: "movable-point-hitbox",
@@ -31406,6 +31407,7 @@ const MovablePoint$1 = props => {
31406
31407
  point,
31407
31408
  onMove = () => {},
31408
31409
  onFocusChange = () => {},
31410
+ onClick = () => {},
31409
31411
  cursor,
31410
31412
  color = wonderBlocksTokens.color.blue,
31411
31413
  constrain = p => snap(snapStep, p)
@@ -31428,7 +31430,10 @@ const MovablePoint$1 = props => {
31428
31430
  tabIndex: 0,
31429
31431
  onFocusChange
31430
31432
  },
31431
- onClick: () => elementRef.current?.focus(),
31433
+ onClick: () => {
31434
+ onClick && onClick();
31435
+ elementRef.current?.focus();
31436
+ },
31432
31437
  cursor: cursor
31433
31438
  });
31434
31439
  };
@@ -31639,6 +31644,11 @@ function getGradableGraph(state, initialGraph) {
31639
31644
  allowReflexAngles: state.allowReflexAngles
31640
31645
  };
31641
31646
  }
31647
+ if (state.type === "none" && initialGraph.type === "none") {
31648
+ return {
31649
+ type: "none"
31650
+ };
31651
+ }
31642
31652
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
31643
31653
  }
31644
31654
 
@@ -32036,12 +32046,18 @@ function UnlimitedPointGraph(props) {
32036
32046
  key: i,
32037
32047
  point: point,
32038
32048
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
32039
- onFocusChange: isFocused => {
32049
+ onFocusChange: (event, isFocused) => {
32040
32050
  if (isFocused) {
32041
32051
  dispatch(actions.pointGraph.focusPoint(i));
32042
32052
  } else {
32053
+ if (event.relatedTarget?.id === REMOVE_BUTTON_ID) {
32054
+ return;
32055
+ }
32043
32056
  dispatch(actions.pointGraph.blurPoint());
32044
32057
  }
32058
+ },
32059
+ onClick: () => {
32060
+ dispatch(actions.pointGraph.clickPoint(i));
32045
32061
  }
32046
32062
  })));
32047
32063
  }
@@ -32200,6 +32216,7 @@ function useDraggablePx(args) {
32200
32216
  * - Protractor
32201
32217
  * - Interactive Graph Elements
32202
32218
  */
32219
+ const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
32203
32220
  const MafsGraph = props => {
32204
32221
  const {
32205
32222
  state,
@@ -32324,10 +32341,10 @@ const MafsGraph = props => {
32324
32341
  zoom: false,
32325
32342
  width: width,
32326
32343
  height: height
32327
- }, props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
32344
+ }, /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
32328
32345
  state,
32329
32346
  dispatch
32330
- }))))), renderGraphControls({
32347
+ })))))), renderGraphControls({
32331
32348
  state,
32332
32349
  dispatch,
32333
32350
  width
@@ -32338,26 +32355,17 @@ const renderPointGraphControls = props => /*#__PURE__*/React__namespace.createEl
32338
32355
  flexDirection: "row",
32339
32356
  width: props.width
32340
32357
  }
32341
- }, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
32342
- kind: "secondary",
32343
- style: {
32344
- width: "100%",
32345
- marginLeft: "20px"
32346
- },
32347
- tabIndex: 0,
32348
- onClick: () => {
32349
- props.dispatch(actions.pointGraph.addPoint([0, 0]));
32350
- }
32351
- }, "Add Point"), (props.state.focusedPointIndex !== null || props.state.previouslyFocusedPointIndex !== null) && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
32358
+ }, props.state.showRemovePointButton && props.state.focusedPointIndex !== null && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
32359
+ id: REMOVE_BUTTON_ID,
32352
32360
  kind: "secondary",
32353
32361
  color: "destructive",
32354
- tabIndex: 0,
32362
+ tabIndex: -1,
32355
32363
  style: {
32356
32364
  width: "100%",
32357
32365
  marginLeft: "20px"
32358
32366
  },
32359
32367
  onClick: event => {
32360
- props.dispatch(actions.pointGraph.removePoint(props.state.previouslyFocusedPointIndex));
32368
+ props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
32361
32369
  }
32362
32370
  }, "Remove Point"));
32363
32371
  const renderGraphControls = props => {
@@ -32492,6 +32500,8 @@ const renderGraph = props => {
32492
32500
  graphState: state,
32493
32501
  dispatch: dispatch
32494
32502
  });
32503
+ case "none":
32504
+ return null;
32495
32505
  default:
32496
32506
  throw new wonderStuffCore.UnreachableCaseError(type);
32497
32507
  }
@@ -32550,7 +32560,7 @@ function initializeGraphState(params) {
32550
32560
  coords: getPointCoords(graph, range, step),
32551
32561
  numPoints: graph.numPoints || 0,
32552
32562
  focusedPointIndex: null,
32553
- previouslyFocusedPointIndex: null
32563
+ showRemovePointButton: false
32554
32564
  };
32555
32565
  case "circle":
32556
32566
  return {
@@ -32584,6 +32594,11 @@ function initializeGraphState(params) {
32584
32594
  allowReflexAngles: Boolean(graph.allowReflexAngles),
32585
32595
  snapDegrees: Number(graph.snapDegrees)
32586
32596
  };
32597
+ case "none":
32598
+ return {
32599
+ ...shared,
32600
+ type: "none"
32601
+ };
32587
32602
  default:
32588
32603
  throw new wonderStuffCore.UnreachableCaseError(graph);
32589
32604
  }
@@ -32816,6 +32831,8 @@ function interactiveGraphReducer(state, action) {
32816
32831
  return doBlurPoint(state);
32817
32832
  case DELETE_INTENT:
32818
32833
  return doDeleteIntent(state);
32834
+ case CLICK_POINT:
32835
+ return doClickPoint(state, action);
32819
32836
  default:
32820
32837
  throw new wonderStuffCore.UnreachableCaseError(action);
32821
32838
  }
@@ -32847,13 +32864,26 @@ function doBlurPoint(state, action) {
32847
32864
  case "point":
32848
32865
  return {
32849
32866
  ...state,
32850
- previouslyFocusedPointIndex: state.focusedPointIndex,
32851
- focusedPointIndex: null
32867
+ focusedPointIndex: null,
32868
+ showRemovePointButton: false
32852
32869
  };
32853
32870
  default:
32854
32871
  return state;
32855
32872
  }
32856
32873
  }
32874
+ function doClickPoint(state, action) {
32875
+ if (state.type !== "point") {
32876
+ return state;
32877
+ }
32878
+ if (state.numPoints === "unlimited") {
32879
+ return {
32880
+ ...state,
32881
+ focusedPointIndex: action.index,
32882
+ showRemovePointButton: true
32883
+ };
32884
+ }
32885
+ return state;
32886
+ }
32857
32887
  function doMovePointInFigure(state, action) {
32858
32888
  switch (state.type) {
32859
32889
  case "segment":
@@ -32894,6 +32924,8 @@ function doMovePointInFigure(state, action) {
32894
32924
  }
32895
32925
  case "angle":
32896
32926
  case "circle":
32927
+ throw new Error("FIXME implement circle reducer");
32928
+ case "none":
32897
32929
  case "point":
32898
32930
  case "polygon":
32899
32931
  case "quadratic":
@@ -33209,7 +33241,8 @@ function doAddPoint(state, action) {
33209
33241
  return {
33210
33242
  ...state,
33211
33243
  hasBeenInteractedWith: true,
33212
- coords: [...state.coords, snappedPoint]
33244
+ coords: [...state.coords, snappedPoint],
33245
+ showRemovePointButton: false
33213
33246
  };
33214
33247
  }
33215
33248
  function doRemovePoint(state, action) {
@@ -33220,7 +33253,7 @@ function doRemovePoint(state, action) {
33220
33253
  ...state,
33221
33254
  coords: state.coords.filter((_, i) => i !== action.index),
33222
33255
  focusedPointIndex: null,
33223
- previouslyFocusedPointIndex: null
33256
+ showRemovePointButton: false
33224
33257
  };
33225
33258
  }
33226
33259
  const getDeltaVertex = (maxMoves, minMoves, delta) => {
@@ -33619,6 +33652,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33619
33652
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
33620
33653
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
33621
33654
  const showSides = graph.type === "polygon" ? graph.showSides : null;
33655
+ const startCoords = "startCoords" in graph ? graph.startCoords : null;
33622
33656
  const originalPropsRef = React.useRef(props);
33623
33657
  const latestPropsRef = wonderBlocksCore.useLatestRef(props);
33624
33658
  React.useEffect(() => {
@@ -33629,7 +33663,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33629
33663
  if (latestPropsRef.current !== originalPropsRef.current) {
33630
33664
  dispatch(reinitialize(latestPropsRef.current));
33631
33665
  }
33632
- }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
33666
+ }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords]);
33633
33667
 
33634
33668
  // If the graph is static, it always displays the correct answer. This is
33635
33669
  // standard behavior for Perseus widgets (e.g. compare the Radio widget).
@@ -33955,6 +33989,8 @@ class LegacyInteractiveGraph extends React__namespace.Component {
33955
33989
  ___default["default"].invoke(this.points, "remove");
33956
33990
  this.line?.remove();
33957
33991
  };
33992
+ addNoneControls = () => {};
33993
+ removeNoneControls = () => {};
33958
33994
  addLinearControls = () => {
33959
33995
  this.addLine("line");
33960
33996
  };
@@ -35157,6 +35193,9 @@ class InteractiveGraph extends React__namespace.Component {
35157
35193
  });
35158
35194
  });
35159
35195
  }
35196
+ static getNoneEquationString() {
35197
+ return "";
35198
+ }
35160
35199
  static getLinearEquationString(props) {
35161
35200
  const coords = InteractiveGraph.getLineCoords(props.graph, props);
35162
35201
  if (eq(coords[0][0], coords[1][0])) {
@@ -35270,6 +35309,16 @@ class InteractiveGraph extends React__namespace.Component {
35270
35309
  return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
35271
35310
  }
35272
35311
  static validate(userInput, rubric, component) {
35312
+ // None-type graphs are not graded
35313
+ if (userInput.type === "none" && rubric.correct.type === "none") {
35314
+ return {
35315
+ type: "points",
35316
+ earned: 0,
35317
+ total: 0,
35318
+ message: null
35319
+ };
35320
+ }
35321
+
35273
35322
  // When nothing has moved, there will neither be coords nor the
35274
35323
  // circle's center/radius fields. When those fields are absent, skip
35275
35324
  // all these checks; just go mark the answer as empty.
@@ -35485,6 +35534,8 @@ function shouldUseMafs(mafsFlags, graph) {
35485
35534
  return false;
35486
35535
  }
35487
35536
  switch (graph.type) {
35537
+ case "none":
35538
+ return true;
35488
35539
  case "point":
35489
35540
  if (graph.numPoints === UNLIMITED) {
35490
35541
  return Boolean(mafsFlags["unlimited-point"]);
@@ -45048,7 +45099,7 @@ const init = function (options) {
45048
45099
 
45049
45100
  // This file is processed by a Rollup plugin (replace) to inject the production
45050
45101
  const libName = "@khanacademy/perseus";
45051
- const libVersion = "33.2.1";
45102
+ const libVersion = "33.3.0";
45052
45103
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45053
45104
 
45054
45105
  const apiVersion = {