@khanacademy/perseus 33.2.0 → 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
@@ -29666,7 +29655,8 @@ function LockedLabel(props) {
29666
29655
  left: x,
29667
29656
  top: y,
29668
29657
  color: lockedFigureColors[color],
29669
- fontSize: font.size[size]
29658
+ fontSize: font.size[size],
29659
+ backgroundColor: "rgba(255, 255, 255, 0.8)"
29670
29660
  }
29671
29661
  }, /*#__PURE__*/React.createElement(TeX, null, text));
29672
29662
  }
@@ -30044,7 +30034,8 @@ const actions = {
30044
30034
  addPoint,
30045
30035
  removePoint,
30046
30036
  focusPoint,
30047
- blurPoint
30037
+ blurPoint,
30038
+ clickPoint
30048
30039
  },
30049
30040
  polygon: {
30050
30041
  movePoint,
@@ -30106,6 +30097,13 @@ function blurPoint() {
30106
30097
  type: BLUR_POINT
30107
30098
  };
30108
30099
  }
30100
+ const CLICK_POINT = "click-point";
30101
+ function clickPoint(index) {
30102
+ return {
30103
+ type: CLICK_POINT,
30104
+ index
30105
+ };
30106
+ }
30109
30107
  const MOVE_ALL = "move-all";
30110
30108
  function moveAll(delta) {
30111
30109
  return {
@@ -30430,10 +30428,14 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
30430
30428
  },
30431
30429
  "data-testid": "movable-point",
30432
30430
  tabIndex: disableKeyboardInteraction ? -1 : tabIndex(focusBehavior),
30433
- onFocus: () => {
30434
- 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);
30435
30438
  },
30436
- onBlur: () => getOnFocusChangeCallback(focusBehavior)(false),
30437
30439
  onClick: onClick
30438
30440
  }, /*#__PURE__*/React.createElement("circle", {
30439
30441
  className: "movable-point-hitbox",
@@ -31079,6 +31081,7 @@ const MovablePoint$1 = props => {
31079
31081
  point,
31080
31082
  onMove = () => {},
31081
31083
  onFocusChange = () => {},
31084
+ onClick = () => {},
31082
31085
  cursor,
31083
31086
  color: color$1 = color.blue,
31084
31087
  constrain = p => snap(snapStep, p)
@@ -31103,7 +31106,8 @@ const MovablePoint$1 = props => {
31103
31106
  },
31104
31107
  onClick: () => {
31105
31108
  var _elementRef$current;
31106
- return (_elementRef$current = elementRef.current) == null ? void 0 : _elementRef$current.focus();
31109
+ onClick && onClick();
31110
+ (_elementRef$current = elementRef.current) == null || _elementRef$current.focus();
31107
31111
  },
31108
31112
  cursor: cursor
31109
31113
  });
@@ -31305,6 +31309,11 @@ function getGradableGraph(state, initialGraph) {
31305
31309
  allowReflexAngles: state.allowReflexAngles
31306
31310
  });
31307
31311
  }
31312
+ if (state.type === "none" && initialGraph.type === "none") {
31313
+ return {
31314
+ type: "none"
31315
+ };
31316
+ }
31308
31317
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
31309
31318
  }
31310
31319
 
@@ -31702,12 +31711,19 @@ function UnlimitedPointGraph(props) {
31702
31711
  key: i,
31703
31712
  point: point,
31704
31713
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
31705
- onFocusChange: isFocused => {
31714
+ onFocusChange: (event, isFocused) => {
31706
31715
  if (isFocused) {
31707
31716
  dispatch(actions.pointGraph.focusPoint(i));
31708
31717
  } else {
31718
+ var _event$relatedTarget;
31719
+ if (((_event$relatedTarget = event.relatedTarget) == null ? void 0 : _event$relatedTarget.id) === REMOVE_BUTTON_ID) {
31720
+ return;
31721
+ }
31709
31722
  dispatch(actions.pointGraph.blurPoint());
31710
31723
  }
31724
+ },
31725
+ onClick: () => {
31726
+ dispatch(actions.pointGraph.clickPoint(i));
31711
31727
  }
31712
31728
  })));
31713
31729
  }
@@ -31855,6 +31871,18 @@ function useDraggablePx(args) {
31855
31871
  });
31856
31872
  }
31857
31873
 
31874
+ /**
31875
+ * Render the Mafs graph with the specified background and graph elements.
31876
+ *
31877
+ * Render order (back to front):
31878
+ * - Grid
31879
+ * - Axis Ticks, Axis Arrows, and Axis Labels
31880
+ * - Locked Figures
31881
+ * - Locked Labels
31882
+ * - Protractor
31883
+ * - Interactive Graph Elements
31884
+ */
31885
+ const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
31858
31886
  const MafsGraph = props => {
31859
31887
  const {
31860
31888
  state,
@@ -31934,8 +31962,6 @@ const MafsGraph = props => {
31934
31962
  }, fullGraphAriaDescription), /*#__PURE__*/React.createElement(LegacyGrid, {
31935
31963
  box: props.box,
31936
31964
  backgroundImage: props.backgroundImage
31937
- }), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
31938
- lockedFigures: props.lockedFigures
31939
31965
  }), /*#__PURE__*/React.createElement(View, {
31940
31966
  style: {
31941
31967
  position: "absolute",
@@ -31965,10 +31991,27 @@ const MafsGraph = props => {
31965
31991
  props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
31966
31992
  lockedFigures: props.lockedFigures,
31967
31993
  range: state.range
31968
- }), props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31994
+ }))), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
31995
+ lockedFigures: props.lockedFigures
31996
+ }), /*#__PURE__*/React.createElement(View, {
31997
+ style: {
31998
+ position: "absolute"
31999
+ }
32000
+ }, /*#__PURE__*/React.createElement(Mafs, {
32001
+ preserveAspectRatio: false,
32002
+ viewBox: {
32003
+ x: state.range[X],
32004
+ y: state.range[Y],
32005
+ padding: 0
32006
+ },
32007
+ pan: false,
32008
+ zoom: false,
32009
+ width: width,
32010
+ height: height
32011
+ }, /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31969
32012
  state,
31970
32013
  dispatch
31971
- }))))), renderGraphControls({
32014
+ })))))), renderGraphControls({
31972
32015
  state,
31973
32016
  dispatch,
31974
32017
  width
@@ -31979,26 +32022,17 @@ const renderPointGraphControls = props => /*#__PURE__*/React.createElement(View,
31979
32022
  flexDirection: "row",
31980
32023
  width: props.width
31981
32024
  }
31982
- }, /*#__PURE__*/React.createElement(Button, {
31983
- kind: "secondary",
31984
- style: {
31985
- width: "100%",
31986
- marginLeft: "20px"
31987
- },
31988
- tabIndex: 0,
31989
- onClick: () => {
31990
- props.dispatch(actions.pointGraph.addPoint([0, 0]));
31991
- }
31992
- }, "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,
31993
32027
  kind: "secondary",
31994
32028
  color: "destructive",
31995
- tabIndex: 0,
32029
+ tabIndex: -1,
31996
32030
  style: {
31997
32031
  width: "100%",
31998
32032
  marginLeft: "20px"
31999
32033
  },
32000
32034
  onClick: event => {
32001
- props.dispatch(actions.pointGraph.removePoint(props.state.previouslyFocusedPointIndex));
32035
+ props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
32002
32036
  }
32003
32037
  }, "Remove Point"));
32004
32038
  const renderGraphControls = props => {
@@ -32133,6 +32167,8 @@ const renderGraph = props => {
32133
32167
  graphState: state,
32134
32168
  dispatch: dispatch
32135
32169
  });
32170
+ case "none":
32171
+ return null;
32136
32172
  default:
32137
32173
  throw new UnreachableCaseError(type);
32138
32174
  }
@@ -32186,7 +32222,7 @@ function initializeGraphState(params) {
32186
32222
  coords: getPointCoords(graph, range, step),
32187
32223
  numPoints: graph.numPoints || 0,
32188
32224
  focusedPointIndex: null,
32189
- previouslyFocusedPointIndex: null
32225
+ showRemovePointButton: false
32190
32226
  });
32191
32227
  case "circle":
32192
32228
  return _extends({}, shared, {
@@ -32215,6 +32251,10 @@ function initializeGraphState(params) {
32215
32251
  allowReflexAngles: Boolean(graph.allowReflexAngles),
32216
32252
  snapDegrees: Number(graph.snapDegrees)
32217
32253
  });
32254
+ case "none":
32255
+ return _extends({}, shared, {
32256
+ type: "none"
32257
+ });
32218
32258
  default:
32219
32259
  throw new UnreachableCaseError(graph);
32220
32260
  }
@@ -32450,6 +32490,8 @@ function interactiveGraphReducer(state, action) {
32450
32490
  return doBlurPoint(state);
32451
32491
  case DELETE_INTENT:
32452
32492
  return doDeleteIntent(state);
32493
+ case CLICK_POINT:
32494
+ return doClickPoint(state, action);
32453
32495
  default:
32454
32496
  throw new UnreachableCaseError(action);
32455
32497
  }
@@ -32479,13 +32521,25 @@ function doBlurPoint(state, action) {
32479
32521
  switch (state.type) {
32480
32522
  case "point":
32481
32523
  return _extends({}, state, {
32482
- previouslyFocusedPointIndex: state.focusedPointIndex,
32483
- focusedPointIndex: null
32524
+ focusedPointIndex: null,
32525
+ showRemovePointButton: false
32484
32526
  });
32485
32527
  default:
32486
32528
  return state;
32487
32529
  }
32488
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
+ }
32489
32543
  function doMovePointInFigure(state, action) {
32490
32544
  switch (state.type) {
32491
32545
  case "segment":
@@ -32524,6 +32578,8 @@ function doMovePointInFigure(state, action) {
32524
32578
  }
32525
32579
  case "angle":
32526
32580
  case "circle":
32581
+ throw new Error("FIXME implement circle reducer");
32582
+ case "none":
32527
32583
  case "point":
32528
32584
  case "polygon":
32529
32585
  case "quadratic":
@@ -32825,7 +32881,8 @@ function doAddPoint(state, action) {
32825
32881
  // If there's no point in spot where we want the new point to go we add it there
32826
32882
  return _extends({}, state, {
32827
32883
  hasBeenInteractedWith: true,
32828
- coords: [...state.coords, snappedPoint]
32884
+ coords: [...state.coords, snappedPoint],
32885
+ showRemovePointButton: false
32829
32886
  });
32830
32887
  }
32831
32888
  function doRemovePoint(state, action) {
@@ -32835,7 +32892,7 @@ function doRemovePoint(state, action) {
32835
32892
  return _extends({}, state, {
32836
32893
  coords: state.coords.filter((_, i) => i !== action.index),
32837
32894
  focusedPointIndex: null,
32838
- previouslyFocusedPointIndex: null
32895
+ showRemovePointButton: false
32839
32896
  });
32840
32897
  }
32841
32898
  const getDeltaVertex = (maxMoves, minMoves, delta) => {
@@ -33219,6 +33276,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
33219
33276
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
33220
33277
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
33221
33278
  const showSides = graph.type === "polygon" ? graph.showSides : null;
33279
+ const startCoords = "startCoords" in graph ? graph.startCoords : null;
33222
33280
  const originalPropsRef = useRef(props);
33223
33281
  const latestPropsRef = useLatestRef(props);
33224
33282
  useEffect(() => {
@@ -33229,7 +33287,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
33229
33287
  if (latestPropsRef.current !== originalPropsRef.current) {
33230
33288
  dispatch(reinitialize(latestPropsRef.current));
33231
33289
  }
33232
- }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
33290
+ }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords]);
33233
33291
 
33234
33292
  // If the graph is static, it always displays the correct answer. This is
33235
33293
  // standard behavior for Perseus widgets (e.g. compare the Radio widget).
@@ -33506,6 +33564,8 @@ class LegacyInteractiveGraph extends React.Component {
33506
33564
  _.invoke(this.points, "remove");
33507
33565
  (_this$line = this.line) == null || _this$line.remove();
33508
33566
  };
33567
+ this.addNoneControls = () => {};
33568
+ this.removeNoneControls = () => {};
33509
33569
  this.addLinearControls = () => {
33510
33570
  this.addLine("line");
33511
33571
  };
@@ -34773,6 +34833,9 @@ class InteractiveGraph extends React.Component {
34773
34833
  });
34774
34834
  });
34775
34835
  }
34836
+ static getNoneEquationString() {
34837
+ return "";
34838
+ }
34776
34839
  static getLinearEquationString(props) {
34777
34840
  const coords = InteractiveGraph.getLineCoords(props.graph, props);
34778
34841
  if (eq(coords[0][0], coords[1][0])) {
@@ -34886,6 +34949,16 @@ class InteractiveGraph extends React.Component {
34886
34949
  return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
34887
34950
  }
34888
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
+
34889
34962
  // When nothing has moved, there will neither be coords nor the
34890
34963
  // circle's center/radius fields. When those fields are absent, skip
34891
34964
  // all these checks; just go mark the answer as empty.
@@ -35115,6 +35188,8 @@ function shouldUseMafs(mafsFlags, graph) {
35115
35188
  return false;
35116
35189
  }
35117
35190
  switch (graph.type) {
35191
+ case "none":
35192
+ return true;
35118
35193
  case "point":
35119
35194
  if (graph.numPoints === UNLIMITED) {
35120
35195
  return Boolean(mafsFlags["unlimited-point"]);
@@ -43098,7 +43173,7 @@ class PhetSimulation extends React.Component {
43098
43173
  className: css(styles$6.iframeResponsive),
43099
43174
  src: (_this$state$url = this.state.url) == null ? void 0 : _this$state$url.toString(),
43100
43175
  allow: "fullscreen"
43101
- })), /*#__PURE__*/React.createElement(IconButton, {
43176
+ })), this.state.url !== null && /*#__PURE__*/React.createElement(IconButton, {
43102
43177
  icon: cornersOutIcon,
43103
43178
  onClick: () => {
43104
43179
  var _this$iframeRef$curre;
@@ -43110,8 +43185,7 @@ class PhetSimulation extends React.Component {
43110
43185
  marginTop: 5,
43111
43186
  marginBottom: 5,
43112
43187
  alignSelf: "flex-end"
43113
- },
43114
- disabled: this.state.url === null
43188
+ }
43115
43189
  }));
43116
43190
  }
43117
43191
  }
@@ -44666,7 +44740,7 @@ const init = function init(options) {
44666
44740
 
44667
44741
  // This file is processed by a Rollup plugin (replace) to inject the production
44668
44742
  const libName = "@khanacademy/perseus";
44669
- const libVersion = "33.2.0";
44743
+ const libVersion = "33.3.0";
44670
44744
  addLibraryVersionToPerseusDebug(libName, libVersion);
44671
44745
 
44672
44746
  const apiVersion = {