@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/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
@@ -29980,7 +29969,8 @@ function LockedLabel(props) {
29980
29969
  left: x,
29981
29970
  top: y,
29982
29971
  color: lockedFigureColors[color],
29983
- fontSize: wonderBlocksTokens.font.size[size]
29972
+ fontSize: wonderBlocksTokens.font.size[size],
29973
+ backgroundColor: "rgba(255, 255, 255, 0.8)"
29984
29974
  }
29985
29975
  }, /*#__PURE__*/React__namespace.createElement(TeX, null, text));
29986
29976
  }
@@ -30358,7 +30348,8 @@ const actions = {
30358
30348
  addPoint,
30359
30349
  removePoint,
30360
30350
  focusPoint,
30361
- blurPoint
30351
+ blurPoint,
30352
+ clickPoint
30362
30353
  },
30363
30354
  polygon: {
30364
30355
  movePoint,
@@ -30420,6 +30411,13 @@ function blurPoint() {
30420
30411
  type: BLUR_POINT
30421
30412
  };
30422
30413
  }
30414
+ const CLICK_POINT = "click-point";
30415
+ function clickPoint(index) {
30416
+ return {
30417
+ type: CLICK_POINT,
30418
+ index
30419
+ };
30420
+ }
30423
30421
  const MOVE_ALL = "move-all";
30424
30422
  function moveAll(delta) {
30425
30423
  return {
@@ -30748,10 +30746,14 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
30748
30746
  },
30749
30747
  "data-testid": "movable-point",
30750
30748
  tabIndex: disableKeyboardInteraction ? -1 : tabIndex(focusBehavior),
30751
- onFocus: () => {
30752
- 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);
30753
30756
  },
30754
- onBlur: () => getOnFocusChangeCallback(focusBehavior)(false),
30755
30757
  onClick: onClick
30756
30758
  }, /*#__PURE__*/React__namespace.createElement("circle", {
30757
30759
  className: "movable-point-hitbox",
@@ -31405,6 +31407,7 @@ const MovablePoint$1 = props => {
31405
31407
  point,
31406
31408
  onMove = () => {},
31407
31409
  onFocusChange = () => {},
31410
+ onClick = () => {},
31408
31411
  cursor,
31409
31412
  color = wonderBlocksTokens.color.blue,
31410
31413
  constrain = p => snap(snapStep, p)
@@ -31427,7 +31430,10 @@ const MovablePoint$1 = props => {
31427
31430
  tabIndex: 0,
31428
31431
  onFocusChange
31429
31432
  },
31430
- onClick: () => elementRef.current?.focus(),
31433
+ onClick: () => {
31434
+ onClick && onClick();
31435
+ elementRef.current?.focus();
31436
+ },
31431
31437
  cursor: cursor
31432
31438
  });
31433
31439
  };
@@ -31638,6 +31644,11 @@ function getGradableGraph(state, initialGraph) {
31638
31644
  allowReflexAngles: state.allowReflexAngles
31639
31645
  };
31640
31646
  }
31647
+ if (state.type === "none" && initialGraph.type === "none") {
31648
+ return {
31649
+ type: "none"
31650
+ };
31651
+ }
31641
31652
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
31642
31653
  }
31643
31654
 
@@ -32035,12 +32046,18 @@ function UnlimitedPointGraph(props) {
32035
32046
  key: i,
32036
32047
  point: point,
32037
32048
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
32038
- onFocusChange: isFocused => {
32049
+ onFocusChange: (event, isFocused) => {
32039
32050
  if (isFocused) {
32040
32051
  dispatch(actions.pointGraph.focusPoint(i));
32041
32052
  } else {
32053
+ if (event.relatedTarget?.id === REMOVE_BUTTON_ID) {
32054
+ return;
32055
+ }
32042
32056
  dispatch(actions.pointGraph.blurPoint());
32043
32057
  }
32058
+ },
32059
+ onClick: () => {
32060
+ dispatch(actions.pointGraph.clickPoint(i));
32044
32061
  }
32045
32062
  })));
32046
32063
  }
@@ -32188,6 +32205,18 @@ function useDraggablePx(args) {
32188
32205
  });
32189
32206
  }
32190
32207
 
32208
+ /**
32209
+ * Render the Mafs graph with the specified background and graph elements.
32210
+ *
32211
+ * Render order (back to front):
32212
+ * - Grid
32213
+ * - Axis Ticks, Axis Arrows, and Axis Labels
32214
+ * - Locked Figures
32215
+ * - Locked Labels
32216
+ * - Protractor
32217
+ * - Interactive Graph Elements
32218
+ */
32219
+ const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
32191
32220
  const MafsGraph = props => {
32192
32221
  const {
32193
32222
  state,
@@ -32266,8 +32295,6 @@ const MafsGraph = props => {
32266
32295
  }, fullGraphAriaDescription), /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
32267
32296
  box: props.box,
32268
32297
  backgroundImage: props.backgroundImage
32269
- }), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
32270
- lockedFigures: props.lockedFigures
32271
32298
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
32272
32299
  style: {
32273
32300
  position: "absolute",
@@ -32297,10 +32324,27 @@ const MafsGraph = props => {
32297
32324
  props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
32298
32325
  lockedFigures: props.lockedFigures,
32299
32326
  range: state.range
32300
- }), props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
32327
+ }))), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
32328
+ lockedFigures: props.lockedFigures
32329
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
32330
+ style: {
32331
+ position: "absolute"
32332
+ }
32333
+ }, /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
32334
+ preserveAspectRatio: false,
32335
+ viewBox: {
32336
+ x: state.range[X],
32337
+ y: state.range[Y],
32338
+ padding: 0
32339
+ },
32340
+ pan: false,
32341
+ zoom: false,
32342
+ width: width,
32343
+ height: height
32344
+ }, /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
32301
32345
  state,
32302
32346
  dispatch
32303
- }))))), renderGraphControls({
32347
+ })))))), renderGraphControls({
32304
32348
  state,
32305
32349
  dispatch,
32306
32350
  width
@@ -32311,26 +32355,17 @@ const renderPointGraphControls = props => /*#__PURE__*/React__namespace.createEl
32311
32355
  flexDirection: "row",
32312
32356
  width: props.width
32313
32357
  }
32314
- }, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
32315
- kind: "secondary",
32316
- style: {
32317
- width: "100%",
32318
- marginLeft: "20px"
32319
- },
32320
- tabIndex: 0,
32321
- onClick: () => {
32322
- props.dispatch(actions.pointGraph.addPoint([0, 0]));
32323
- }
32324
- }, "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,
32325
32360
  kind: "secondary",
32326
32361
  color: "destructive",
32327
- tabIndex: 0,
32362
+ tabIndex: -1,
32328
32363
  style: {
32329
32364
  width: "100%",
32330
32365
  marginLeft: "20px"
32331
32366
  },
32332
32367
  onClick: event => {
32333
- props.dispatch(actions.pointGraph.removePoint(props.state.previouslyFocusedPointIndex));
32368
+ props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
32334
32369
  }
32335
32370
  }, "Remove Point"));
32336
32371
  const renderGraphControls = props => {
@@ -32465,6 +32500,8 @@ const renderGraph = props => {
32465
32500
  graphState: state,
32466
32501
  dispatch: dispatch
32467
32502
  });
32503
+ case "none":
32504
+ return null;
32468
32505
  default:
32469
32506
  throw new wonderStuffCore.UnreachableCaseError(type);
32470
32507
  }
@@ -32523,7 +32560,7 @@ function initializeGraphState(params) {
32523
32560
  coords: getPointCoords(graph, range, step),
32524
32561
  numPoints: graph.numPoints || 0,
32525
32562
  focusedPointIndex: null,
32526
- previouslyFocusedPointIndex: null
32563
+ showRemovePointButton: false
32527
32564
  };
32528
32565
  case "circle":
32529
32566
  return {
@@ -32557,6 +32594,11 @@ function initializeGraphState(params) {
32557
32594
  allowReflexAngles: Boolean(graph.allowReflexAngles),
32558
32595
  snapDegrees: Number(graph.snapDegrees)
32559
32596
  };
32597
+ case "none":
32598
+ return {
32599
+ ...shared,
32600
+ type: "none"
32601
+ };
32560
32602
  default:
32561
32603
  throw new wonderStuffCore.UnreachableCaseError(graph);
32562
32604
  }
@@ -32789,6 +32831,8 @@ function interactiveGraphReducer(state, action) {
32789
32831
  return doBlurPoint(state);
32790
32832
  case DELETE_INTENT:
32791
32833
  return doDeleteIntent(state);
32834
+ case CLICK_POINT:
32835
+ return doClickPoint(state, action);
32792
32836
  default:
32793
32837
  throw new wonderStuffCore.UnreachableCaseError(action);
32794
32838
  }
@@ -32820,13 +32864,26 @@ function doBlurPoint(state, action) {
32820
32864
  case "point":
32821
32865
  return {
32822
32866
  ...state,
32823
- previouslyFocusedPointIndex: state.focusedPointIndex,
32824
- focusedPointIndex: null
32867
+ focusedPointIndex: null,
32868
+ showRemovePointButton: false
32825
32869
  };
32826
32870
  default:
32827
32871
  return state;
32828
32872
  }
32829
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
+ }
32830
32887
  function doMovePointInFigure(state, action) {
32831
32888
  switch (state.type) {
32832
32889
  case "segment":
@@ -32867,6 +32924,8 @@ function doMovePointInFigure(state, action) {
32867
32924
  }
32868
32925
  case "angle":
32869
32926
  case "circle":
32927
+ throw new Error("FIXME implement circle reducer");
32928
+ case "none":
32870
32929
  case "point":
32871
32930
  case "polygon":
32872
32931
  case "quadratic":
@@ -33182,7 +33241,8 @@ function doAddPoint(state, action) {
33182
33241
  return {
33183
33242
  ...state,
33184
33243
  hasBeenInteractedWith: true,
33185
- coords: [...state.coords, snappedPoint]
33244
+ coords: [...state.coords, snappedPoint],
33245
+ showRemovePointButton: false
33186
33246
  };
33187
33247
  }
33188
33248
  function doRemovePoint(state, action) {
@@ -33193,7 +33253,7 @@ function doRemovePoint(state, action) {
33193
33253
  ...state,
33194
33254
  coords: state.coords.filter((_, i) => i !== action.index),
33195
33255
  focusedPointIndex: null,
33196
- previouslyFocusedPointIndex: null
33256
+ showRemovePointButton: false
33197
33257
  };
33198
33258
  }
33199
33259
  const getDeltaVertex = (maxMoves, minMoves, delta) => {
@@ -33592,6 +33652,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33592
33652
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
33593
33653
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
33594
33654
  const showSides = graph.type === "polygon" ? graph.showSides : null;
33655
+ const startCoords = "startCoords" in graph ? graph.startCoords : null;
33595
33656
  const originalPropsRef = React.useRef(props);
33596
33657
  const latestPropsRef = wonderBlocksCore.useLatestRef(props);
33597
33658
  React.useEffect(() => {
@@ -33602,7 +33663,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33602
33663
  if (latestPropsRef.current !== originalPropsRef.current) {
33603
33664
  dispatch(reinitialize(latestPropsRef.current));
33604
33665
  }
33605
- }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
33666
+ }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords]);
33606
33667
 
33607
33668
  // If the graph is static, it always displays the correct answer. This is
33608
33669
  // standard behavior for Perseus widgets (e.g. compare the Radio widget).
@@ -33928,6 +33989,8 @@ class LegacyInteractiveGraph extends React__namespace.Component {
33928
33989
  ___default["default"].invoke(this.points, "remove");
33929
33990
  this.line?.remove();
33930
33991
  };
33992
+ addNoneControls = () => {};
33993
+ removeNoneControls = () => {};
33931
33994
  addLinearControls = () => {
33932
33995
  this.addLine("line");
33933
33996
  };
@@ -35130,6 +35193,9 @@ class InteractiveGraph extends React__namespace.Component {
35130
35193
  });
35131
35194
  });
35132
35195
  }
35196
+ static getNoneEquationString() {
35197
+ return "";
35198
+ }
35133
35199
  static getLinearEquationString(props) {
35134
35200
  const coords = InteractiveGraph.getLineCoords(props.graph, props);
35135
35201
  if (eq(coords[0][0], coords[1][0])) {
@@ -35243,6 +35309,16 @@ class InteractiveGraph extends React__namespace.Component {
35243
35309
  return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
35244
35310
  }
35245
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
+
35246
35322
  // When nothing has moved, there will neither be coords nor the
35247
35323
  // circle's center/radius fields. When those fields are absent, skip
35248
35324
  // all these checks; just go mark the answer as empty.
@@ -35458,6 +35534,8 @@ function shouldUseMafs(mafsFlags, graph) {
35458
35534
  return false;
35459
35535
  }
35460
35536
  switch (graph.type) {
35537
+ case "none":
35538
+ return true;
35461
35539
  case "point":
35462
35540
  if (graph.numPoints === UNLIMITED) {
35463
35541
  return Boolean(mafsFlags["unlimited-point"]);
@@ -43459,7 +43537,7 @@ class PhetSimulation extends React__namespace.Component {
43459
43537
  className: aphrodite.css(styles$6.iframeResponsive),
43460
43538
  src: this.state.url?.toString(),
43461
43539
  allow: "fullscreen"
43462
- })), /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
43540
+ })), this.state.url !== null && /*#__PURE__*/React__namespace.createElement(IconButton__default["default"], {
43463
43541
  icon: cornersOutIcon__default["default"],
43464
43542
  onClick: () => {
43465
43543
  this.iframeRef.current?.requestFullscreen();
@@ -43470,8 +43548,7 @@ class PhetSimulation extends React__namespace.Component {
43470
43548
  marginTop: 5,
43471
43549
  marginBottom: 5,
43472
43550
  alignSelf: "flex-end"
43473
- },
43474
- disabled: this.state.url === null
43551
+ }
43475
43552
  }));
43476
43553
  }
43477
43554
  }
@@ -45022,7 +45099,7 @@ const init = function (options) {
45022
45099
 
45023
45100
  // This file is processed by a Rollup plugin (replace) to inject the production
45024
45101
  const libName = "@khanacademy/perseus";
45025
- const libVersion = "33.2.0";
45102
+ const libVersion = "33.3.0";
45026
45103
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45027
45104
 
45028
45105
  const apiVersion = {