@khanacademy/perseus 23.0.0 → 23.1.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.d.ts CHANGED
@@ -87,7 +87,7 @@ export type { ILogger, LogErrorOptions, ErrorKind } from "./logging/log";
87
87
  export type { ServerItemRenderer as ServerItemRendererComponent } from "./server-item-renderer";
88
88
  export type { Alignment, APIOptions, APIOptionsWithDefaults, ChangeHandler, DeviceType, DomInsertCheckFn, EditorMode, FocusPath, Hint, ImageDict, ImageUploader, JiptLabelStore, JiptRenderer, PerseusDependencies, PerseusScore, Version, VideoData, VideoKind, WidgetDict, WidgetExports, } from "./types";
89
89
  export type { ParsedValue } from "./util";
90
- export type { LockedFigure, LockedFigureColor, LockedFigureType, LockedPointType, LockedLineType, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions, Range, Size, MathFormat, InputNumberWidget, // TODO(jeremy): remove?
90
+ export type { LockedFigure, LockedFigureColor, LockedFigureType, LockedPointType, LockedLineType, LockedCircleType, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions, Range, Size, MathFormat, InputNumberWidget, // TODO(jeremy): remove?
91
91
  PerseusImageBackground, PerseusInputNumberWidgetOptions, PerseusInteractiveGraphWidgetOptions, PerseusItem, PerseusPlotterWidgetOptions, PerseusPythonProgramWidgetOptions, PerseusRadioWidgetOptions, PerseusExampleWidgetOptions, PerseusExampleGraphieWidgetOptions, PerseusSimpleMarkdownTesterWidgetOptions, PerseusRenderer, PerseusWidget, PerseusWidgetsMap, } from "./perseus-types";
92
92
  export type { Coord } from "./interactive2/types";
93
93
  export type { MarkerType } from "./widgets/label-image/types";
package/dist/index.js CHANGED
@@ -17598,16 +17598,8 @@ class SvgImage extends React__namespace.Component {
17598
17598
  // Props to send to all images
17599
17599
  const imageProps = {
17600
17600
  alt: this.props.alt,
17601
- title: this.props.title,
17602
- tabIndex: "0"
17601
+ title: this.props.title
17603
17602
  };
17604
- // If alt text is present, AND overrideAriaHidden is true,
17605
- // include aria-hidden to avoid screen reader from crawling texts
17606
- // both from the div meant for screen readers (see svg-image.jsx)
17607
- // and alt attribute.
17608
- if (this.props.alt && this.props.overrideAriaHidden) {
17609
- imageProps["aria-hidden"] = true;
17610
- }
17611
17603
  const width = this.props.width && this.props.width * this.props.scale;
17612
17604
  const height = this.props.height && this.props.height * this.props.scale;
17613
17605
  const dimensions = {
@@ -27649,7 +27641,7 @@ class ImageWidget extends React__namespace.Component {
27649
27641
 
27650
27642
  render() {
27651
27643
  let image;
27652
- let alt;
27644
+ const alt = this.props.caption === this.props.alt ? "" : this.props.alt;
27653
27645
  const {
27654
27646
  apiOptions
27655
27647
  } = this.props;
@@ -27662,29 +27654,7 @@ class ImageWidget extends React__namespace.Component {
27662
27654
  } = _ref;
27663
27655
  return /*#__PURE__*/React__namespace.createElement(SvgImage, {
27664
27656
  src: url,
27665
- alt:
27666
- /* alt text is formatted in a sr-only
27667
- div next to the image in addition to
27668
- the alt attribute.
27669
- If there is no alt text at all,
27670
- we don't put an alt attribute on
27671
- the image, so that screen readers
27672
- know there's something they can't
27673
- read there :(.
27674
- NOTE: React <=0.13 (maybe later)
27675
- has a bug where it won't ever
27676
- remove an attribute, so if this
27677
- alt node is ever defined it's
27678
- not removed. This is sort of
27679
- dangerous, but we usually re-key
27680
- new renderers so that they're
27681
- rendered from scratch anyways,
27682
- so this shouldn't be a problem
27683
- in practice right now, although
27684
- it will exhibit weird behaviour
27685
- while editing. */
27686
- this.props.alt,
27687
- overrideAriaHidden: true,
27657
+ alt: alt,
27688
27658
  width: backgroundImage.width,
27689
27659
  height: backgroundImage.height,
27690
27660
  preloader: apiOptions.imagePreloader,
@@ -27701,16 +27671,6 @@ class ImageWidget extends React__namespace.Component {
27701
27671
  });
27702
27672
  });
27703
27673
  }
27704
- if (this.props.alt) {
27705
- alt = /*#__PURE__*/React__namespace.createElement("span", {
27706
- className: "perseus-sr-only"
27707
- }, /*#__PURE__*/React__namespace.createElement(Renderer, {
27708
- content: this.props.alt,
27709
- apiOptions: apiOptions,
27710
- linterContext: this.props.linterContext,
27711
- strings: this.context.strings
27712
- }));
27713
- }
27714
27674
 
27715
27675
  // For mobile we combine an image's title and caption.
27716
27676
  if (apiOptions.isMobile) {
@@ -27777,7 +27737,7 @@ class ImageWidget extends React__namespace.Component {
27777
27737
  style: {
27778
27738
  maxWidth: backgroundImage.width
27779
27739
  }
27780
- }, image, alt, titleAndCaption);
27740
+ }, image, titleAndCaption);
27781
27741
  }
27782
27742
  let title;
27783
27743
  let caption;
@@ -27809,7 +27769,7 @@ class ImageWidget extends React__namespace.Component {
27809
27769
  style: {
27810
27770
  maxWidth: backgroundImage.width
27811
27771
  }
27812
- }, title, image, alt, caption);
27772
+ }, title, image, caption);
27813
27773
  }
27814
27774
  static validate(userInput, rubric) {
27815
27775
  return {
@@ -28964,6 +28924,28 @@ const lockedFigureColors = {
28964
28924
  };
28965
28925
  const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
28966
28926
 
28927
+ const LockedCircle = props => {
28928
+ const {
28929
+ center,
28930
+ radius,
28931
+ color,
28932
+ fillStyle,
28933
+ strokeStyle
28934
+ } = props;
28935
+ const fill = {
28936
+ none: 0,
28937
+ solid: 1,
28938
+ translucent: 0.4
28939
+ };
28940
+ return /*#__PURE__*/React__namespace.createElement(mafs.Circle, {
28941
+ center: center,
28942
+ radius: radius,
28943
+ fillOpacity: fill[fillStyle],
28944
+ strokeStyle: strokeStyle,
28945
+ color: lockedFigureColors[color]
28946
+ });
28947
+ };
28948
+
28967
28949
  function pathBuilder() {
28968
28950
  return new PathBuilder();
28969
28951
  }
@@ -29232,6 +29214,10 @@ const GraphLockedLayer = props => {
29232
29214
  key: `line-${index}`,
29233
29215
  range: props.range
29234
29216
  }, figure));
29217
+ case "circle":
29218
+ return /*#__PURE__*/React__namespace.createElement(LockedCircle, _extends({
29219
+ key: `circle-${index}`
29220
+ }, figure));
29235
29221
  default:
29236
29222
  /**
29237
29223
  * Devlopment-time future-proofing: This should
@@ -30043,8 +30029,13 @@ function initializeGraphState(params) {
30043
30029
  type: graph.type,
30044
30030
  coords: getQuadraticCoords(graph, range, step)
30045
30031
  };
30046
- case "angle":
30047
30032
  case "sinusoid":
30033
+ return {
30034
+ ...shared,
30035
+ type: graph.type,
30036
+ coords: getSinusoidCoords(graph, range, step)
30037
+ };
30038
+ case "angle":
30048
30039
  throw new Error("Mafs not yet implemented for graph type: " + graph.type);
30049
30040
  default:
30050
30041
  throw new wonderStuffCore.UnreachableCaseError(graph);
@@ -30148,6 +30139,12 @@ function getGradableGraph(state, initialGraph) {
30148
30139
  coords: state.coords
30149
30140
  };
30150
30141
  }
30142
+ if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
30143
+ return {
30144
+ ...initialGraph,
30145
+ coords: state.coords
30146
+ };
30147
+ }
30151
30148
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
30152
30149
  }
30153
30150
  const getDefaultSegments = _ref2 => {
@@ -30227,6 +30224,11 @@ const getPolygonCoords = _ref4 => {
30227
30224
  coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
30228
30225
  return coords;
30229
30226
  };
30227
+ const getSinusoidCoords = (graph, range, step) => {
30228
+ let coords = [[0.5, 0.5], [0.65, 0.6]];
30229
+ coords = normalizePoints(range, step, coords, true);
30230
+ return coords;
30231
+ };
30230
30232
  const getQuadraticCoords = (graph, range, step) => {
30231
30233
  let coords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
30232
30234
  coords = normalizePoints(range, step, coords, true);
@@ -30417,6 +30419,62 @@ const getQuadraticCoefficients = coords => {
30417
30419
  return [a, b, c];
30418
30420
  };
30419
30421
 
30422
+ function SinusoidGraph(props) {
30423
+ const {
30424
+ dispatch,
30425
+ graphState
30426
+ } = props;
30427
+
30428
+ // Destructure the coordinates from the graph state
30429
+ // Note: The order of the coordinates is important:
30430
+ // The coords[0] is the root and the coords[1] is the first peak
30431
+ const {
30432
+ coords
30433
+ } = graphState;
30434
+
30435
+ // Get the coefficients for calculating the quadratic equation
30436
+ const coeffs = getSinusoidCoefficients(coords);
30437
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
30438
+ y: x => computeSine(x, coeffs),
30439
+ color: wonderBlocksTokens.color.blue
30440
+ }), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
30441
+ key: "point-" + i,
30442
+ point: coord,
30443
+ onMove: destination => dispatch(movePoint(i, destination))
30444
+ })));
30445
+ }
30446
+
30447
+ // Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
30448
+ const computeSine = function (x,
30449
+ // x-coordinate
30450
+ sinusoidCoefficients) {
30451
+ // Break down the coefficients for the sine function to improve readability
30452
+ const {
30453
+ amplitude: a,
30454
+ angularFrequency: b,
30455
+ phase: c,
30456
+ verticalOffset: d
30457
+ } = sinusoidCoefficients;
30458
+ return a * Math.sin(b * x - c) + d;
30459
+ };
30460
+ const getSinusoidCoefficients = coords => {
30461
+ // It's assumed that p1 is the root and p2 is the first peak
30462
+ const p1 = coords[0];
30463
+ const p2 = coords[1];
30464
+
30465
+ // Resulting coefficients are canonical for this sine curve
30466
+ const amplitude = p2[1] - p1[1];
30467
+ const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
30468
+ const phase = p1[0] * angularFrequency;
30469
+ const verticalOffset = p1[1];
30470
+ return {
30471
+ amplitude,
30472
+ angularFrequency,
30473
+ phase,
30474
+ verticalOffset
30475
+ };
30476
+ };
30477
+
30420
30478
  // Universal padding for the axis ticks to ensure that the labels
30421
30479
  // have enough space to render without overlapping the tick lines
30422
30480
  const tickPadding = 5;
@@ -31550,6 +31608,7 @@ function doMoveControlPoint(state, action) {
31550
31608
  case "point":
31551
31609
  case "polygon":
31552
31610
  case "quadratic":
31611
+ case "sinusoid":
31553
31612
  throw new Error(`Don't use moveControlPoint for ${state.type} graphs. Use movePoint instead!`);
31554
31613
  default:
31555
31614
  throw new wonderStuffCore.UnreachableCaseError(state);
@@ -31639,6 +31698,30 @@ function doMovePoint(state, action) {
31639
31698
  })
31640
31699
  };
31641
31700
  }
31701
+ case "sinusoid":
31702
+ {
31703
+ // First, we need to verify that the new coordinates are not on the same vertical line
31704
+ // If they are, we don't want to move the point
31705
+ const destination = action.destination;
31706
+ const newCoords = [...state.coords];
31707
+ newCoords[action.index] = action.destination;
31708
+ if (newCoords[0][0] === newCoords[1][0]) {
31709
+ return state;
31710
+ }
31711
+ return {
31712
+ ...state,
31713
+ hasBeenInteractedWith: true,
31714
+ coords: setAtIndex({
31715
+ array: state.coords,
31716
+ index: action.index,
31717
+ newValue: snap(state.snapStep, bound$1({
31718
+ snapStep: state.snapStep,
31719
+ range: state.range,
31720
+ point: destination
31721
+ }))
31722
+ })
31723
+ };
31724
+ }
31642
31725
  case "quadratic":
31643
31726
  {
31644
31727
  return {
@@ -31883,6 +31966,11 @@ const renderGraph = props => {
31883
31966
  graphState: state,
31884
31967
  dispatch: dispatch
31885
31968
  });
31969
+ case "sinusoid":
31970
+ return /*#__PURE__*/React__namespace.createElement(SinusoidGraph, {
31971
+ graphState: state,
31972
+ dispatch: dispatch
31973
+ });
31886
31974
  default:
31887
31975
  return new wonderStuffCore.UnreachableCaseError(type);
31888
31976
  }
@@ -44096,7 +44184,7 @@ const init = function (options) {
44096
44184
 
44097
44185
  // This file is processed by a Rollup plugin (replace) to inject the production
44098
44186
  const libName = "@khanacademy/perseus";
44099
- const libVersion = "23.0.0";
44187
+ const libVersion = "23.1.0";
44100
44188
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
44101
44189
 
44102
44190
  const apiVersion = {