@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/es/index.js CHANGED
@@ -23743,9 +23743,8 @@ const BaseRadio = function BaseRadio(props) {
23743
23743
  // element) We have to add some extra behavior to make
23744
23744
  // sure that we can still check the choice.
23745
23745
  let listElem = null;
23746
- let clickHandler = null;
23746
+ let clickHandler;
23747
23747
  if (editMode) {
23748
- // @ts-expect-error - TS2322 - Type '(e: any) => void' is not assignable to type 'null'.
23749
23748
  clickHandler = e => {
23750
23749
  // Traverse the parent nodes of the clicked
23751
23750
  // element.
@@ -23772,19 +23771,11 @@ const BaseRadio = function BaseRadio(props) {
23772
23771
  // somehow break something happening inside a choice's
23773
23772
  // child Renderers, by changing when we mount/unmount?
23774
23773
  return /*#__PURE__*/React.createElement("li", {
23775
- key: i
23776
- // @ts-expect-error - TS2322 - Type 'HTMLLIElement | null' is not assignable to type 'null'.
23777
- ,
23778
- ref: e => listElem = e
23779
- // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'string'.
23780
- ,
23781
- className: className
23782
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'MouseEventHandler<HTMLLIElement> | undefined'.
23783
- ,
23784
- onClick: clickHandler
23785
- // @ts-expect-error - TS2322 - Type '((e: TouchEvent) => void) | null' is not assignable to type 'TouchEventHandler<HTMLLIElement> | undefined'.
23786
- ,
23787
- onTouchStart: !labelWrap ? null : captureScratchpadTouchStart$1
23774
+ key: i,
23775
+ ref: e => listElem = e,
23776
+ className: className,
23777
+ onClick: clickHandler,
23778
+ onTouchStart: labelWrap ? undefined : captureScratchpadTouchStart$1
23788
23779
  }, /*#__PURE__*/React.createElement(Element, _extends({}, elementProps, {
23789
23780
  ref: ref
23790
23781
  })));
@@ -29386,9 +29377,7 @@ class NumberInput extends React.Component {
29386
29377
  onFocus: this._handleFocus,
29387
29378
  onBlur: this._handleBlur,
29388
29379
  onKeyPress: this._handleBlur,
29389
- onKeyDown: this._onKeyDown
29390
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
29391
- ,
29380
+ onKeyDown: this._onKeyDown,
29392
29381
  onTouchStart: captureScratchpadTouchStart,
29393
29382
  defaultValue: toNumericString(this.props.value, this.state.format),
29394
29383
  value: undefined
@@ -30045,7 +30034,8 @@ const actions = {
30045
30034
  addPoint,
30046
30035
  removePoint,
30047
30036
  focusPoint,
30048
- blurPoint
30037
+ blurPoint,
30038
+ clickPoint
30049
30039
  },
30050
30040
  polygon: {
30051
30041
  movePoint,
@@ -30107,6 +30097,13 @@ function blurPoint() {
30107
30097
  type: BLUR_POINT
30108
30098
  };
30109
30099
  }
30100
+ const CLICK_POINT = "click-point";
30101
+ function clickPoint(index) {
30102
+ return {
30103
+ type: CLICK_POINT,
30104
+ index
30105
+ };
30106
+ }
30110
30107
  const MOVE_ALL = "move-all";
30111
30108
  function moveAll(delta) {
30112
30109
  return {
@@ -30431,10 +30428,14 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
30431
30428
  },
30432
30429
  "data-testid": "movable-point",
30433
30430
  tabIndex: disableKeyboardInteraction ? -1 : tabIndex(focusBehavior),
30434
- onFocus: () => {
30435
- return getOnFocusChangeCallback(focusBehavior)(true);
30431
+ onFocus: event => {
30432
+ const callback = getOnFocusChangeCallback(focusBehavior);
30433
+ callback && callback(event, true);
30434
+ },
30435
+ onBlur: event => {
30436
+ const callback = getOnFocusChangeCallback(focusBehavior);
30437
+ callback && callback(event, false);
30436
30438
  },
30437
- onBlur: () => getOnFocusChangeCallback(focusBehavior)(false),
30438
30439
  onClick: onClick
30439
30440
  }, /*#__PURE__*/React.createElement("circle", {
30440
30441
  className: "movable-point-hitbox",
@@ -31080,6 +31081,7 @@ const MovablePoint$1 = props => {
31080
31081
  point,
31081
31082
  onMove = () => {},
31082
31083
  onFocusChange = () => {},
31084
+ onClick = () => {},
31083
31085
  cursor,
31084
31086
  color: color$1 = color.blue,
31085
31087
  constrain = p => snap(snapStep, p)
@@ -31104,7 +31106,8 @@ const MovablePoint$1 = props => {
31104
31106
  },
31105
31107
  onClick: () => {
31106
31108
  var _elementRef$current;
31107
- return (_elementRef$current = elementRef.current) == null ? void 0 : _elementRef$current.focus();
31109
+ onClick && onClick();
31110
+ (_elementRef$current = elementRef.current) == null || _elementRef$current.focus();
31108
31111
  },
31109
31112
  cursor: cursor
31110
31113
  });
@@ -31306,6 +31309,11 @@ function getGradableGraph(state, initialGraph) {
31306
31309
  allowReflexAngles: state.allowReflexAngles
31307
31310
  });
31308
31311
  }
31312
+ if (state.type === "none" && initialGraph.type === "none") {
31313
+ return {
31314
+ type: "none"
31315
+ };
31316
+ }
31309
31317
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
31310
31318
  }
31311
31319
 
@@ -31703,12 +31711,19 @@ function UnlimitedPointGraph(props) {
31703
31711
  key: i,
31704
31712
  point: point,
31705
31713
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
31706
- onFocusChange: isFocused => {
31714
+ onFocusChange: (event, isFocused) => {
31707
31715
  if (isFocused) {
31708
31716
  dispatch(actions.pointGraph.focusPoint(i));
31709
31717
  } else {
31718
+ var _event$relatedTarget;
31719
+ if (((_event$relatedTarget = event.relatedTarget) == null ? void 0 : _event$relatedTarget.id) === REMOVE_BUTTON_ID) {
31720
+ return;
31721
+ }
31710
31722
  dispatch(actions.pointGraph.blurPoint());
31711
31723
  }
31724
+ },
31725
+ onClick: () => {
31726
+ dispatch(actions.pointGraph.clickPoint(i));
31712
31727
  }
31713
31728
  })));
31714
31729
  }
@@ -31867,6 +31882,7 @@ function useDraggablePx(args) {
31867
31882
  * - Protractor
31868
31883
  * - Interactive Graph Elements
31869
31884
  */
31885
+ const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
31870
31886
  const MafsGraph = props => {
31871
31887
  const {
31872
31888
  state,
@@ -31992,10 +32008,10 @@ const MafsGraph = props => {
31992
32008
  zoom: false,
31993
32009
  width: width,
31994
32010
  height: height
31995
- }, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
32011
+ }, /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31996
32012
  state,
31997
32013
  dispatch
31998
- }))))), renderGraphControls({
32014
+ })))))), renderGraphControls({
31999
32015
  state,
32000
32016
  dispatch,
32001
32017
  width
@@ -32006,26 +32022,17 @@ const renderPointGraphControls = props => /*#__PURE__*/React.createElement(View,
32006
32022
  flexDirection: "row",
32007
32023
  width: props.width
32008
32024
  }
32009
- }, /*#__PURE__*/React.createElement(Button, {
32010
- kind: "secondary",
32011
- style: {
32012
- width: "100%",
32013
- marginLeft: "20px"
32014
- },
32015
- tabIndex: 0,
32016
- onClick: () => {
32017
- props.dispatch(actions.pointGraph.addPoint([0, 0]));
32018
- }
32019
- }, "Add Point"), (props.state.focusedPointIndex !== null || props.state.previouslyFocusedPointIndex !== null) && /*#__PURE__*/React.createElement(Button, {
32025
+ }, props.state.showRemovePointButton && props.state.focusedPointIndex !== null && /*#__PURE__*/React.createElement(Button, {
32026
+ id: REMOVE_BUTTON_ID,
32020
32027
  kind: "secondary",
32021
32028
  color: "destructive",
32022
- tabIndex: 0,
32029
+ tabIndex: -1,
32023
32030
  style: {
32024
32031
  width: "100%",
32025
32032
  marginLeft: "20px"
32026
32033
  },
32027
32034
  onClick: event => {
32028
- props.dispatch(actions.pointGraph.removePoint(props.state.previouslyFocusedPointIndex));
32035
+ props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
32029
32036
  }
32030
32037
  }, "Remove Point"));
32031
32038
  const renderGraphControls = props => {
@@ -32160,6 +32167,8 @@ const renderGraph = props => {
32160
32167
  graphState: state,
32161
32168
  dispatch: dispatch
32162
32169
  });
32170
+ case "none":
32171
+ return null;
32163
32172
  default:
32164
32173
  throw new UnreachableCaseError(type);
32165
32174
  }
@@ -32213,7 +32222,7 @@ function initializeGraphState(params) {
32213
32222
  coords: getPointCoords(graph, range, step),
32214
32223
  numPoints: graph.numPoints || 0,
32215
32224
  focusedPointIndex: null,
32216
- previouslyFocusedPointIndex: null
32225
+ showRemovePointButton: false
32217
32226
  });
32218
32227
  case "circle":
32219
32228
  return _extends({}, shared, {
@@ -32242,6 +32251,10 @@ function initializeGraphState(params) {
32242
32251
  allowReflexAngles: Boolean(graph.allowReflexAngles),
32243
32252
  snapDegrees: Number(graph.snapDegrees)
32244
32253
  });
32254
+ case "none":
32255
+ return _extends({}, shared, {
32256
+ type: "none"
32257
+ });
32245
32258
  default:
32246
32259
  throw new UnreachableCaseError(graph);
32247
32260
  }
@@ -32477,6 +32490,8 @@ function interactiveGraphReducer(state, action) {
32477
32490
  return doBlurPoint(state);
32478
32491
  case DELETE_INTENT:
32479
32492
  return doDeleteIntent(state);
32493
+ case CLICK_POINT:
32494
+ return doClickPoint(state, action);
32480
32495
  default:
32481
32496
  throw new UnreachableCaseError(action);
32482
32497
  }
@@ -32506,13 +32521,25 @@ function doBlurPoint(state, action) {
32506
32521
  switch (state.type) {
32507
32522
  case "point":
32508
32523
  return _extends({}, state, {
32509
- previouslyFocusedPointIndex: state.focusedPointIndex,
32510
- focusedPointIndex: null
32524
+ focusedPointIndex: null,
32525
+ showRemovePointButton: false
32511
32526
  });
32512
32527
  default:
32513
32528
  return state;
32514
32529
  }
32515
32530
  }
32531
+ function doClickPoint(state, action) {
32532
+ if (state.type !== "point") {
32533
+ return state;
32534
+ }
32535
+ if (state.numPoints === "unlimited") {
32536
+ return _extends({}, state, {
32537
+ focusedPointIndex: action.index,
32538
+ showRemovePointButton: true
32539
+ });
32540
+ }
32541
+ return state;
32542
+ }
32516
32543
  function doMovePointInFigure(state, action) {
32517
32544
  switch (state.type) {
32518
32545
  case "segment":
@@ -32551,6 +32578,8 @@ function doMovePointInFigure(state, action) {
32551
32578
  }
32552
32579
  case "angle":
32553
32580
  case "circle":
32581
+ throw new Error("FIXME implement circle reducer");
32582
+ case "none":
32554
32583
  case "point":
32555
32584
  case "polygon":
32556
32585
  case "quadratic":
@@ -32852,7 +32881,8 @@ function doAddPoint(state, action) {
32852
32881
  // If there's no point in spot where we want the new point to go we add it there
32853
32882
  return _extends({}, state, {
32854
32883
  hasBeenInteractedWith: true,
32855
- coords: [...state.coords, snappedPoint]
32884
+ coords: [...state.coords, snappedPoint],
32885
+ showRemovePointButton: false
32856
32886
  });
32857
32887
  }
32858
32888
  function doRemovePoint(state, action) {
@@ -32862,7 +32892,7 @@ function doRemovePoint(state, action) {
32862
32892
  return _extends({}, state, {
32863
32893
  coords: state.coords.filter((_, i) => i !== action.index),
32864
32894
  focusedPointIndex: null,
32865
- previouslyFocusedPointIndex: null
32895
+ showRemovePointButton: false
32866
32896
  });
32867
32897
  }
32868
32898
  const getDeltaVertex = (maxMoves, minMoves, delta) => {
@@ -33246,6 +33276,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
33246
33276
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
33247
33277
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
33248
33278
  const showSides = graph.type === "polygon" ? graph.showSides : null;
33279
+ const startCoords = "startCoords" in graph ? graph.startCoords : null;
33249
33280
  const originalPropsRef = useRef(props);
33250
33281
  const latestPropsRef = useLatestRef(props);
33251
33282
  useEffect(() => {
@@ -33256,7 +33287,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
33256
33287
  if (latestPropsRef.current !== originalPropsRef.current) {
33257
33288
  dispatch(reinitialize(latestPropsRef.current));
33258
33289
  }
33259
- }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
33290
+ }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords]);
33260
33291
 
33261
33292
  // If the graph is static, it always displays the correct answer. This is
33262
33293
  // standard behavior for Perseus widgets (e.g. compare the Radio widget).
@@ -33533,6 +33564,8 @@ class LegacyInteractiveGraph extends React.Component {
33533
33564
  _.invoke(this.points, "remove");
33534
33565
  (_this$line = this.line) == null || _this$line.remove();
33535
33566
  };
33567
+ this.addNoneControls = () => {};
33568
+ this.removeNoneControls = () => {};
33536
33569
  this.addLinearControls = () => {
33537
33570
  this.addLine("line");
33538
33571
  };
@@ -34800,6 +34833,9 @@ class InteractiveGraph extends React.Component {
34800
34833
  });
34801
34834
  });
34802
34835
  }
34836
+ static getNoneEquationString() {
34837
+ return "";
34838
+ }
34803
34839
  static getLinearEquationString(props) {
34804
34840
  const coords = InteractiveGraph.getLineCoords(props.graph, props);
34805
34841
  if (eq(coords[0][0], coords[1][0])) {
@@ -34913,6 +34949,16 @@ class InteractiveGraph extends React.Component {
34913
34949
  return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
34914
34950
  }
34915
34951
  static validate(userInput, rubric, component) {
34952
+ // None-type graphs are not graded
34953
+ if (userInput.type === "none" && rubric.correct.type === "none") {
34954
+ return {
34955
+ type: "points",
34956
+ earned: 0,
34957
+ total: 0,
34958
+ message: null
34959
+ };
34960
+ }
34961
+
34916
34962
  // When nothing has moved, there will neither be coords nor the
34917
34963
  // circle's center/radius fields. When those fields are absent, skip
34918
34964
  // all these checks; just go mark the answer as empty.
@@ -35142,6 +35188,8 @@ function shouldUseMafs(mafsFlags, graph) {
35142
35188
  return false;
35143
35189
  }
35144
35190
  switch (graph.type) {
35191
+ case "none":
35192
+ return true;
35145
35193
  case "point":
35146
35194
  if (graph.numPoints === UNLIMITED) {
35147
35195
  return Boolean(mafsFlags["unlimited-point"]);
@@ -44692,7 +44740,7 @@ const init = function init(options) {
44692
44740
 
44693
44741
  // This file is processed by a Rollup plugin (replace) to inject the production
44694
44742
  const libName = "@khanacademy/perseus";
44695
- const libVersion = "33.2.1";
44743
+ const libVersion = "33.3.0";
44696
44744
  addLibraryVersionToPerseusDebug(libName, libVersion);
44697
44745
 
44698
44746
  const apiVersion = {