@khanacademy/perseus 22.7.0 → 23.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.css CHANGED
@@ -4177,6 +4177,7 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4177
4177
  --mafs-origin-color: var(--mafs-fg);
4178
4178
  --mafs-line-color: #555;
4179
4179
  --mafs-line-stroke-dash-style: 4, 3;
4180
+ --mafs-axis-stroke-width: 1px;
4180
4181
  --grid-line-subdivision-color: #222;
4181
4182
 
4182
4183
  --mafs-red: #f11d0e;
@@ -4488,7 +4489,7 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4488
4489
  }
4489
4490
 
4490
4491
  .MafsView pattern g {
4491
- stroke: #909296;
4492
+ stroke: rgba(33, 36, 44, 0.32);
4492
4493
  }
4493
4494
  .axis-tick-labels {
4494
4495
  font-size: 14px;
@@ -4524,19 +4525,6 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4524
4525
  transform: translateY(calc(-100% + 0.25em));
4525
4526
  }
4526
4527
 
4527
- .x-axis-tick-labels span,
4528
- .y-axis-tick-labels span {
4529
- /* The negative sign is rendered as a pseudo-element to
4530
- ensure that we are positioning all labels correctly */
4531
- &::before {
4532
- content: attr(data-content);
4533
- position: absolute;
4534
- /* We're translating the symbol up by 1px to ensure it's legible around gridlines. */
4535
- transform: translate(-100%, -1px);
4536
- font-weight: 600; /* This helps get the symbol to render crisply */
4537
- }
4538
- }
4539
-
4540
4528
  .y-axis-tick-labels span {
4541
4529
  display: inline-block;
4542
4530
  height: var(--y-axis-label-height, 20px);
package/dist/es/index.js CHANGED
@@ -29,7 +29,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
29
29
  import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
30
30
  import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
31
31
  import debounce$2 from 'lodash.debounce';
32
- import { vec, Line as Line$4, Point as Point$2, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
32
+ import { vec, Line as Line$4, Point as Point$2, useMovable, Text as Text$1, Polygon, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
33
33
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
34
34
  import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
35
35
  import questionIcon from '@phosphor-icons/core/regular/question.svg';
@@ -1142,8 +1142,6 @@ const ApiOptions = {
1142
1142
  // drawing area (scratchpad) should be allowed/disallowed.
1143
1143
  // Previously handled by `Khan.scratchpad.enable/disable`
1144
1144
  setDrawingAreaAvailable: PropTypes.func,
1145
- // Whether to use the Draft.js editor or the legacy textarea
1146
- useDraftEditor: PropTypes.bool,
1147
1145
  // The color used for the hint progress indicator (eg. 1 / 3)
1148
1146
  hintProgressColor: PropTypes.string,
1149
1147
  // Whether this Renderer is allowed to auto-scroll the rest of the
@@ -1153,8 +1151,6 @@ const ApiOptions = {
1153
1151
  //
1154
1152
  // Defaults to `false`.
1155
1153
  canScrollPage: PropTypes.bool,
1156
- // Whether or not we are rendering content inside of a modal.
1157
- inModal: PropTypes.bool,
1158
1154
  // Whether to enable the cross-out feature on multiple-choice radio
1159
1155
  // widgets. This allows users to note which answers they believe to
1160
1156
  // be incorrect, to find the answer by process of elimination.
@@ -1190,9 +1186,7 @@ const ApiOptions = {
1190
1186
  }
1191
1187
  },
1192
1188
  setDrawingAreaAvailable: function () {},
1193
- useDraftEditor: false,
1194
1189
  canScrollPage: false,
1195
- inModal: false,
1196
1190
  crossOutEnabled: false,
1197
1191
  editorChangeDelay: 0
1198
1192
  }
@@ -24950,8 +24944,6 @@ var CSProgram$1 = {
24950
24944
  hidden: true
24951
24945
  };
24952
24946
 
24953
- // TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
24954
-
24955
24947
  class Definition extends React.Component {
24956
24948
  constructor(...args) {
24957
24949
  super(...args);
@@ -25166,8 +25158,6 @@ var Dropdown$1 = {
25166
25158
 
25167
25159
  /* eslint-disable react/sort-comp */
25168
25160
 
25169
- // TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
25170
-
25171
25161
  // transform = _.identity
25172
25162
 
25173
25163
  function mediaQueryIsMatched(mediaQuery) {
@@ -28577,7 +28567,6 @@ function AxisLabels() {
28577
28567
 
28578
28568
  // Range is replaced within this file with Interval, but it is used elsewhere
28579
28569
  // and exported from the package, so we need to keep it around.
28580
- // TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
28581
28570
  const ItemExtras = [
28582
28571
  // The user might benefit from using a Scientific Calculator. Provided on Khan Academy when true
28583
28572
  "calculator",
@@ -29742,9 +29731,13 @@ function initializeGraphState(params) {
29742
29731
  center: [0, 0],
29743
29732
  radiusPoint: [1, 0]
29744
29733
  });
29734
+ case "quadratic":
29735
+ return _extends({}, shared, {
29736
+ type: graph.type,
29737
+ coords: getQuadraticCoords(graph, range, step)
29738
+ });
29745
29739
  case "angle":
29746
29740
  case "sinusoid":
29747
- case "quadratic":
29748
29741
  throw new Error("Mafs not yet implemented for graph type: " + graph.type);
29749
29742
  default:
29750
29743
  throw new UnreachableCaseError(graph);
@@ -29833,6 +29826,11 @@ function getGradableGraph(state, initialGraph) {
29833
29826
  radius: getRadius(state)
29834
29827
  });
29835
29828
  }
29829
+ if (state.type === "quadratic" && initialGraph.type === "quadratic") {
29830
+ return _extends({}, initialGraph, {
29831
+ coords: state.coords
29832
+ });
29833
+ }
29836
29834
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
29837
29835
  }
29838
29836
  const getDefaultSegments = ({
@@ -29910,6 +29908,11 @@ const getPolygonCoords = ({
29910
29908
  coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
29911
29909
  return coords;
29912
29910
  };
29911
+ const getQuadraticCoords = (graph, range, step) => {
29912
+ let coords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
29913
+ coords = normalizePoints(range, step, coords, true);
29914
+ return coords;
29915
+ };
29913
29916
 
29914
29917
  /**
29915
29918
  * determine radius of a circle graph
@@ -30019,6 +30022,82 @@ function crossProduct(as, bs) {
30019
30022
  return result;
30020
30023
  }
30021
30024
 
30025
+ function QuadraticGraph(props) {
30026
+ const {
30027
+ dispatch,
30028
+ graphState
30029
+ } = props;
30030
+ const {
30031
+ coords
30032
+ } = graphState;
30033
+
30034
+ // The coefficients are used to calculate the quadratic equation, plot the graph, and to indicate
30035
+ // to content creators the currently selected "correct answer". ex: y = 0.200x^2 + 0.000x + 0.000
30036
+ // While we should technically never have invalid coordinates, we want to ensure that we have a fallback
30037
+ const coeffRef = React.useRef([0, 0, 0]);
30038
+ const coeffs = getQuadraticCoefficients(coords);
30039
+ if (coeffs !== undefined) {
30040
+ coeffRef.current = coeffs;
30041
+ }
30042
+
30043
+ // Destructure the coefficients for calculating the quadratic equation
30044
+ const [a, b, c] = coeffRef.current;
30045
+
30046
+ // Calculate the y value based on the current x value
30047
+ const y = x => (a * x + b) * x + c;
30048
+ const handleOnMove = (destination, elementId) => {
30049
+ // If the destination is invalid, we want do not want to move the point
30050
+ const validDestination = isValidDestination(destination, elementId, coords);
30051
+ if (validDestination === false) {
30052
+ return;
30053
+ }
30054
+ dispatch(movePoint(elementId, destination));
30055
+ };
30056
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Plot$3.OfX, {
30057
+ y: y,
30058
+ color: color.blue
30059
+ }), coords.map((coord, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
30060
+ key: "point-" + i,
30061
+ point: coord,
30062
+ onMove: destination => handleOnMove(destination, i)
30063
+ })));
30064
+ }
30065
+
30066
+ // Ensure that we are only snapping to coordinates that result in a valid quadratic equation
30067
+ const isValidDestination = (destination, elementId, coords) => {
30068
+ // Set up the new coords and check if the quadratic coefficients are valid
30069
+ const newCoords = [...coords];
30070
+ newCoords[elementId] = destination;
30071
+ const QuadraticCoefficients = getQuadraticCoefficients(newCoords);
30072
+
30073
+ // If the new destination results in an invalid quadratic equation, we don't want to move the point
30074
+ if (QuadraticCoefficients === undefined) {
30075
+ return false;
30076
+ }
30077
+ return true;
30078
+ };
30079
+
30080
+ // Get the quadratic coefficients from the 3 control points
30081
+ // These equations were originally set up in 2013 and may require some
30082
+ // additional comments to help clarify the quadratic formula manipulations
30083
+ // Origin: https://phabricator.khanacademy.org/D2413
30084
+ const getQuadraticCoefficients = coords => {
30085
+ const p1 = coords[0];
30086
+ const p2 = coords[1];
30087
+ const p3 = coords[2];
30088
+
30089
+ // If the denominator is 0, we are going to return undefined as we are
30090
+ // unable to calculate the quadratic coefficients when they hit infinity
30091
+ const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
30092
+ if (denom === 0) {
30093
+ return;
30094
+ }
30095
+ const a = (p3[0] * (p2[1] - p1[1]) + p2[0] * (p1[1] - p3[1]) + p1[0] * (p3[1] - p2[1])) / denom;
30096
+ const b = (p3[0] * p3[0] * (p1[1] - p2[1]) + p2[0] * p2[0] * (p3[1] - p1[1]) + p1[0] * p1[0] * (p2[1] - p3[1])) / denom;
30097
+ const c = (p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] + p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] + p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) / denom;
30098
+ return [a, b, c];
30099
+ };
30100
+
30022
30101
  // Universal padding for the axis ticks to ensure that the labels
30023
30102
  // have enough space to render without overlapping the tick lines
30024
30103
  const tickPadding = 5;
@@ -30178,9 +30257,6 @@ const AxisTickLabel = ({
30178
30257
  const pointOnAxis = axis === "x" ? [label, 0] : [0, label];
30179
30258
  const pixelPoint = pointToPixel(pointOnAxis, graphConfig);
30180
30259
 
30181
- // Remove the negative sign from the label string as it is handled in the data-content attribute
30182
- const labelString = Math.abs(label).toString();
30183
-
30184
30260
  // Determine the relevant edge of the graph based on the axis type
30185
30261
  const graphEdge = axis === "x" ? graphConfig.width : graphConfig.height;
30186
30262
 
@@ -30196,15 +30272,9 @@ const AxisTickLabel = ({
30196
30272
  gridStep: gridStep[vectorIndex],
30197
30273
  tickStep: tickStep[vectorIndex]
30198
30274
  });
30199
-
30200
- // Determine whether or not to render a negative symbol for the tick label
30201
- // as the negative sign is handled in the data-content attribute in order to
30202
- // maintain the correct spacing between the labels
30203
- const dataContent = label < 0 && shouldShowLabel ? "−" : null;
30204
30275
  return /*#__PURE__*/React.createElement("span", {
30205
- className: `${axis}-axis-tick-label`,
30206
- "data-content": dataContent
30207
- }, shouldShowLabel && labelString);
30276
+ className: `${axis}-axis-tick-label`
30277
+ }, shouldShowLabel && label);
30208
30278
  };
30209
30279
 
30210
30280
  // Determine whether or not to show the tick label text
@@ -31153,6 +31223,7 @@ function doMoveControlPoint(state, action) {
31153
31223
  throw new Error("FIXME implement circle reducer");
31154
31224
  case "point":
31155
31225
  case "polygon":
31226
+ case "quadratic":
31156
31227
  throw new Error(`Don't use moveControlPoint for ${state.type} graphs. Use movePoint instead!`);
31157
31228
  default:
31158
31229
  throw new UnreachableCaseError(state);
@@ -31223,8 +31294,23 @@ function doMoveAll(state, action) {
31223
31294
  }
31224
31295
  function doMovePoint(state, action) {
31225
31296
  switch (state.type) {
31226
- case "point":
31227
31297
  case "polygon":
31298
+ case "point":
31299
+ {
31300
+ return _extends({}, state, {
31301
+ hasBeenInteractedWith: true,
31302
+ coords: setAtIndex({
31303
+ array: state.coords,
31304
+ index: action.index,
31305
+ newValue: snap(state.snapStep, bound$1({
31306
+ snapStep: state.snapStep,
31307
+ range: state.range,
31308
+ point: action.destination
31309
+ }))
31310
+ })
31311
+ });
31312
+ }
31313
+ case "quadratic":
31228
31314
  {
31229
31315
  return _extends({}, state, {
31230
31316
  hasBeenInteractedWith: true,
@@ -31240,7 +31326,7 @@ function doMovePoint(state, action) {
31240
31326
  });
31241
31327
  }
31242
31328
  default:
31243
- throw new Error("The movePoint action is only for point and polygon graphs");
31329
+ throw new Error("The movePoint action is only for point, quadratic, and polygon graphs");
31244
31330
  }
31245
31331
  }
31246
31332
  function doMoveCenter(state, action) {
@@ -31453,6 +31539,11 @@ const renderGraph = props => {
31453
31539
  graphState: state,
31454
31540
  dispatch: dispatch
31455
31541
  });
31542
+ case "quadratic":
31543
+ return /*#__PURE__*/React.createElement(QuadraticGraph, {
31544
+ graphState: state,
31545
+ dispatch: dispatch
31546
+ });
31456
31547
  default:
31457
31548
  return new UnreachableCaseError(type);
31458
31549
  }
@@ -43667,7 +43758,7 @@ const init = function init(options) {
43667
43758
 
43668
43759
  // This file is processed by a Rollup plugin (replace) to inject the production
43669
43760
  const libName = "@khanacademy/perseus";
43670
- const libVersion = "22.7.0";
43761
+ const libVersion = "23.0.0";
43671
43762
  addLibraryVersionToPerseusDebug(libName, libVersion);
43672
43763
 
43673
43764
  const apiVersion = {