@khanacademy/perseus 21.2.2 → 21.3.1

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
@@ -4291,9 +4291,13 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4291
4291
  --movable-line-stroke-weight-active: 4px;
4292
4292
  }
4293
4293
 
4294
- .MafsView .movable-line:is(:focus-visible, :hover),
4294
+ .MafsView > svg {
4295
+ /* Chrome/Safari bugfix for LEMS-1906 */
4296
+ display: block;
4297
+ }
4298
+
4299
+ .MafsView .movable-line:hover,
4295
4300
  .movable-dragging {
4296
- outline: none;
4297
4301
  --movable-line-stroke-weight: var(--movable-line-stroke-weight-active);
4298
4302
  }
4299
4303
 
@@ -4302,6 +4306,36 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4302
4306
  outline: none;
4303
4307
  }
4304
4308
 
4309
+ .MafsView
4310
+ .movable-line
4311
+ :is(.movable-line-focus-outline, .movable-line-focus-outline-gap) {
4312
+ stroke: transparent;
4313
+ }
4314
+
4315
+ .MafsView .movable-line:focus-visible .movable-line-focus-outline {
4316
+ stroke: var(--mafs-blue);
4317
+ stroke-width: 10px;
4318
+ }
4319
+
4320
+ .MafsView .movable-line:focus-visible .movable-line-focus-outline-gap {
4321
+ stroke: white;
4322
+ stroke-width: 6px;
4323
+ }
4324
+
4325
+ .MafsView .movable-point {
4326
+ cursor: grab;
4327
+ touch-action: none;
4328
+ outline: none;
4329
+ }
4330
+
4331
+ .MafsView .movable-point.movable-point--dragging {
4332
+ cursor: grabbing;
4333
+ }
4334
+
4335
+ .MafsView .movable-point.movable-point--dragging .movable-point-halo {
4336
+ opacity: 0;
4337
+ }
4338
+
4305
4339
  .MafsView .movable-point-hitbox {
4306
4340
  fill: transparent;
4307
4341
  }
@@ -4313,7 +4347,9 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4313
4347
  .movable-point-halo,
4314
4348
  .movable-point-focus-outline
4315
4349
  ) {
4316
- transition: r 0.15s ease-out;
4350
+ transition:
4351
+ r 0.15s ease-out,
4352
+ opacity 0.15s ease-out;
4317
4353
  }
4318
4354
 
4319
4355
  .MafsView .movable-point-center {
@@ -4372,3 +4408,14 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4372
4408
  .MafsView .movable-point:focus-visible .movable-point-focus-outline {
4373
4409
  visibility: visible;
4374
4410
  }
4411
+
4412
+ @font-face {
4413
+ font-family: Mafs-MJXTEX;
4414
+ src: url("https://cdn.kastatic.org/fonts/mathjax/MathJax_Main-Regular.woff")
4415
+ format("woff");
4416
+ }
4417
+
4418
+ .MafsView .axis-ticks text {
4419
+ font-size: 14px;
4420
+ font-family: "Mafs-MJXTEX";
4421
+ }
package/dist/es/index.js CHANGED
@@ -7,7 +7,7 @@ import * as i18n from '@khanacademy/wonder-blocks-i18n';
7
7
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
8
8
  import classNames from 'classnames';
9
9
  import * as React from 'react';
10
- import React__default, { useState, useEffect, useRef, createContext } from 'react';
10
+ import React__default, { useState, useEffect, useRef, createContext, useImperativeHandle } from 'react';
11
11
  import _, { debounce as debounce$1 } from 'underscore';
12
12
  import Clickable from '@khanacademy/wonder-blocks-clickable';
13
13
  import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
@@ -28239,14 +28239,6 @@ function changeRange(range) {
28239
28239
  };
28240
28240
  }
28241
28241
 
28242
- const GraphStateContext = /*#__PURE__*/createContext({
28243
- state: {},
28244
- dispatch: () => {}
28245
- });
28246
- function useGraphState() {
28247
- return React__default.useContext(GraphStateContext);
28248
- }
28249
-
28250
28242
  /**
28251
28243
  * 44 is touch best practice and AAA compliant for WCAG
28252
28244
  * https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
@@ -28332,9 +28324,6 @@ const getLines = points => points.map((point, i) => {
28332
28324
 
28333
28325
  const defaultStroke = "var(--movable-line-stroke-color)";
28334
28326
  const MovableLine = props => {
28335
- const {
28336
- state
28337
- } = useGraphState();
28338
28327
  const {
28339
28328
  start,
28340
28329
  end,
@@ -28359,12 +28348,13 @@ const MovableLine = props => {
28359
28348
  onMove: newPoint => {
28360
28349
  onMove(vec.sub(newPoint, midpoint));
28361
28350
  },
28362
- constrain: p => snap(state.snapStep, p)
28351
+ constrain: p => p
28363
28352
  });
28364
28353
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("g", {
28365
28354
  ref: line,
28366
28355
  tabIndex: 0,
28367
28356
  className: "movable-line",
28357
+ "data-test-id": "movable-line",
28368
28358
  style: {
28369
28359
  cursor: dragging ? "grabbing" : "grab"
28370
28360
  }
@@ -28375,6 +28365,16 @@ const MovableLine = props => {
28375
28365
  stroke: "transparent",
28376
28366
  strokeWidth: TARGET_SIZE
28377
28367
  }
28368
+ }), /*#__PURE__*/React.createElement(SVGLine, {
28369
+ start: startPtPx,
28370
+ end: endPtPx,
28371
+ className: "movable-line-focus-outline",
28372
+ style: {}
28373
+ }), /*#__PURE__*/React.createElement(SVGLine, {
28374
+ start: startPtPx,
28375
+ end: endPtPx,
28376
+ className: "movable-line-focus-outline-gap",
28377
+ style: {}
28378
28378
  }), /*#__PURE__*/React.createElement(SVGLine, {
28379
28379
  start: startPtPx,
28380
28380
  end: endPtPx,
@@ -28382,7 +28382,8 @@ const MovableLine = props => {
28382
28382
  stroke,
28383
28383
  strokeWidth: "var(--movable-line-stroke-weight)"
28384
28384
  },
28385
- dragging: dragging
28385
+ dragging: dragging,
28386
+ testId: "movable-line__line"
28386
28387
  })), startExtend && /*#__PURE__*/React.createElement(Vector, {
28387
28388
  tail: start,
28388
28389
  tip: startExtend,
@@ -28398,18 +28399,30 @@ function SVGLine(props) {
28398
28399
  start,
28399
28400
  end,
28400
28401
  style,
28401
- dragging
28402
+ dragging,
28403
+ className,
28404
+ testId
28402
28405
  } = props;
28406
+ const draggingClass = dragging ? "movable-dragging" : "";
28403
28407
  return /*#__PURE__*/React.createElement("line", {
28404
28408
  x1: start[0],
28405
28409
  y1: start[1],
28406
28410
  x2: end[0],
28407
28411
  y2: end[1],
28408
28412
  style: style,
28409
- className: dragging ? "movable-dragging" : undefined
28413
+ className: `${className} ${draggingClass}`,
28414
+ "data-test-id": testId
28410
28415
  });
28411
28416
  }
28412
28417
 
28418
+ const GraphStateContext = /*#__PURE__*/createContext({
28419
+ state: {},
28420
+ dispatch: () => {}
28421
+ });
28422
+ function useGraphState() {
28423
+ return React__default.useContext(GraphStateContext);
28424
+ }
28425
+
28413
28426
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
28414
28427
  // graph.
28415
28428
  const hitboxSizePx = 48;
@@ -28431,17 +28444,16 @@ const StyledMovablePoint = props => {
28431
28444
  onMove,
28432
28445
  constrain: p => snap(state.snapStep, p)
28433
28446
  });
28447
+ const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""}`;
28434
28448
  const [[x, y]] = useTransform(point);
28435
28449
  return /*#__PURE__*/React.createElement("g", {
28436
28450
  ref: hitboxRef,
28437
- className: "movable-point",
28451
+ className: pointClasses,
28438
28452
  tabIndex: 0,
28439
28453
  style: {
28440
- cursor: dragging ? "grabbing" : "grab",
28441
- touchAction: "none",
28442
- outline: "none",
28443
28454
  "--movable-point-color": color$1
28444
- }
28455
+ },
28456
+ "data-test-id": "movable-point"
28445
28457
  }, /*#__PURE__*/React.createElement("circle", {
28446
28458
  className: "movable-point-hitbox",
28447
28459
  r: hitboxSizePx / 2,
@@ -28465,7 +28477,8 @@ const StyledMovablePoint = props => {
28465
28477
  cy: y,
28466
28478
  style: {
28467
28479
  fill: color$1
28468
- }
28480
+ },
28481
+ "data-test-id": "movable-point__center"
28469
28482
  }));
28470
28483
  };
28471
28484
 
@@ -28922,7 +28935,13 @@ const YGridTick = ({
28922
28935
  x2: xPosition + tickSize / 2,
28923
28936
  y2: yPosition,
28924
28937
  style: tickStyle
28925
- }));
28938
+ }), y !== -1 && /*#__PURE__*/React.createElement("text", {
28939
+ height: 20,
28940
+ width: 50,
28941
+ textAnchor: "end",
28942
+ x: xPosition - 10,
28943
+ y: yPosition + 5
28944
+ }, y.toString()));
28926
28945
  };
28927
28946
  const XGridTick = ({
28928
28947
  x
@@ -28937,13 +28956,23 @@ const XGridTick = ({
28937
28956
  x2: xPosition,
28938
28957
  y2: yPosition - tickSize / 2,
28939
28958
  style: tickStyle
28940
- }));
28959
+ }), x !== -1 && /*#__PURE__*/React.createElement("text", {
28960
+ height: 20,
28961
+ width: 50,
28962
+ textAnchor: "middle",
28963
+ x: xPosition,
28964
+ y: yPosition + 25
28965
+ }, x.toString()));
28941
28966
  };
28942
28967
  function generateTickLocations(tickStep, min, max) {
28943
28968
  const ticks = [];
28969
+
28970
+ // Add ticks in the positive direction
28944
28971
  for (let i = 0 + tickStep; i < max; i += tickStep) {
28945
28972
  ticks.push(i);
28946
28973
  }
28974
+
28975
+ // Add ticks in the negative direction
28947
28976
  for (let i = 0 - tickStep; i > min; i -= tickStep) {
28948
28977
  ticks.push(i);
28949
28978
  }
@@ -28972,24 +29001,6 @@ const AxisTicks = props => {
28972
29001
  }));
28973
29002
  };
28974
29003
 
28975
- /**
28976
- * lineLabelText get the text that should
28977
- * be displayed for a specific tick place
28978
- *
28979
- * @param {number} n the tick number
28980
- * @param {number} tickStep how frequently a label should appear
28981
- * @param {vec.Vector2} range the min/max range on the axis
28982
- */
28983
- const lineLabelText = (n, tickStep, range) => {
28984
- const [min, max] = range;
28985
- const isOnStep = n % tickStep === 0;
28986
- const isNegativeOne = n === -1;
28987
- const isMin = n === min;
28988
- const isMax = n === max;
28989
- const shouldRender = isOnStep && !isNegativeOne && !isMin && !isMax;
28990
- return shouldRender ? `${n}` : "";
28991
- };
28992
-
28993
29004
  /**
28994
29005
  * axisOptions determine axis options for Mafs
28995
29006
  *
@@ -29001,12 +29012,10 @@ const lineLabelText = (n, tickStep, range) => {
29001
29012
  * @param {number} axisIndex which axis we're getting options for
29002
29013
  */
29003
29014
  const axisOptions = (props, axisIndex) => {
29004
- const axisStep = props.tickStep[axisIndex];
29005
- const axisRange = props.range[axisIndex];
29006
29015
  return {
29007
29016
  axis: props.markings === "graph",
29008
29017
  lines: props.gridStep[axisIndex],
29009
- labels: n => lineLabelText(n, axisStep, axisRange)
29018
+ labels: false
29010
29019
  };
29011
29020
  };
29012
29021
  const Grid = props => {
@@ -30234,9 +30243,22 @@ const renderGraph = props => {
30234
30243
  return new UnreachableCaseError(type);
30235
30244
  }
30236
30245
  };
30237
- const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30238
- const [width, height] = props.box;
30246
+ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30239
30247
  const [state, dispatch] = React.useReducer(interactiveGraphReducer, props, initializeGraphState);
30248
+ useImperativeHandle(ref, () => ({
30249
+ getUserInput: () => getGradableGraph(state, props.graph)
30250
+ }));
30251
+ return /*#__PURE__*/React.createElement(MafsGraph, _extends({
30252
+ state: state,
30253
+ dispatch: dispatch
30254
+ }, props));
30255
+ });
30256
+ const MafsGraph = props => {
30257
+ const {
30258
+ state,
30259
+ dispatch
30260
+ } = props;
30261
+ const [width, height] = props.box;
30240
30262
  const prevState = useRef(state);
30241
30263
  useEffect(() => {
30242
30264
  if (prevState.current !== state) {
@@ -30251,16 +30273,13 @@ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30251
30273
  const [xSnap, ySnap] = props.snapStep;
30252
30274
  useEffect(() => {
30253
30275
  dispatch(changeSnapStep([xSnap, ySnap]));
30254
- }, [xSnap, ySnap]);
30276
+ }, [dispatch, xSnap, ySnap]);
30255
30277
 
30256
30278
  // Destructuring first to keep useEffect from making excess calls
30257
30279
  const [[xMinRange, xMaxRange], [yMinRange, yMaxRange]] = props.range;
30258
30280
  useEffect(() => {
30259
30281
  dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
30260
- }, [xMinRange, xMaxRange, yMinRange, yMaxRange]);
30261
- React.useImperativeHandle(ref, () => ({
30262
- getUserInput: () => getGradableGraph(state, props.graph)
30263
- }));
30282
+ }, [dispatch, xMinRange, xMaxRange, yMinRange, yMaxRange]);
30264
30283
  return /*#__PURE__*/React.createElement(GraphStateContext.Provider, {
30265
30284
  value: {
30266
30285
  state,
@@ -30270,7 +30289,9 @@ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30270
30289
  style: {
30271
30290
  width,
30272
30291
  height,
30273
- position: "relative"
30292
+ position: "relative",
30293
+ padding: "25px 25px 0 0",
30294
+ boxSizing: "content-box"
30274
30295
  }
30275
30296
  }, /*#__PURE__*/React.createElement(LegacyGrid, {
30276
30297
  box: props.box,
@@ -30304,7 +30325,7 @@ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30304
30325
  state,
30305
30326
  dispatch
30306
30327
  })))));
30307
- });
30328
+ };
30308
30329
 
30309
30330
  const {
30310
30331
  DeprecationMixin
@@ -31608,7 +31629,7 @@ class InteractiveGraph extends React.Component {
31608
31629
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
31609
31630
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
31610
31631
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
31611
- return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, this.props, {
31632
+ return /*#__PURE__*/React.createElement(StatefulMafsGraph, _extends({}, this.props, {
31612
31633
  ref: this.mafsRef,
31613
31634
  gridStep: gridStep,
31614
31635
  snapStep: snapStep,
@@ -42347,7 +42368,7 @@ const init = function init(options) {
42347
42368
 
42348
42369
  // This file is processed by a Rollup plugin (replace) to inject the production
42349
42370
  const libName = "@khanacademy/perseus";
42350
- const libVersion = "21.2.2";
42371
+ const libVersion = "21.3.1";
42351
42372
  addLibraryVersionToPerseusDebug(libName, libVersion);
42352
42373
 
42353
42374
  const apiVersion = {