@khanacademy/perseus 20.3.1 → 20.4.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/index.js CHANGED
@@ -14406,7 +14406,7 @@ const DEFAULT_STATE$2 = {
14406
14406
  visibleShape: null,
14407
14407
  mouseTarget: null
14408
14408
  };
14409
- const MovableLine$3 = function (graphie, movable, options) {
14409
+ const MovableLine$4 = function (graphie, movable, options) {
14410
14410
  assert$6(graphie != null);
14411
14411
  assert$6(options != null);
14412
14412
 
@@ -14424,10 +14424,10 @@ const MovableLine$3 = function (graphie, movable, options) {
14424
14424
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
14425
14425
  this.modify(___default["default"].extend({}, DEFAULT_STATE$2, options));
14426
14426
  };
14427
- ___default["default"].extend(MovableLine$3, MovableLineOptions);
14428
- InteractiveUtil.createGettersFor(MovableLine$3, ___default["default"].extend({}, DEFAULT_PROPS$2, DEFAULT_STATE$2));
14429
- InteractiveUtil.addMovableHelperMethodsTo(MovableLine$3);
14430
- ___default["default"].extend(MovableLine$3.prototype, {
14427
+ ___default["default"].extend(MovableLine$4, MovableLineOptions);
14428
+ InteractiveUtil.createGettersFor(MovableLine$4, ___default["default"].extend({}, DEFAULT_PROPS$2, DEFAULT_STATE$2));
14429
+ InteractiveUtil.addMovableHelperMethodsTo(MovableLine$4);
14430
+ ___default["default"].extend(MovableLine$4.prototype, {
14431
14431
  cloneState: function () {
14432
14432
  return ___default["default"].extend(this.movable.cloneState(), {
14433
14433
  coords: this.coords()
@@ -15008,7 +15008,7 @@ const DEFAULT_STATE$1 = {
15008
15008
  // TODO(benchristel): this is a memory leak. We add items to
15009
15009
  // tooltipResetFunctions, but never remove them.
15010
15010
  const tooltipResetFunctions = [];
15011
- class MovablePoint$5 {
15011
+ class MovablePoint$4 {
15012
15012
  // @ts-expect-error - TS2564: Property 'prevState' has no initializer and is not definitely assigned in the constructor.
15013
15013
 
15014
15014
  _listenerMap = {};
@@ -16136,15 +16136,15 @@ ___default["default"].extend(MovablePolygon.prototype, {
16136
16136
  });
16137
16137
 
16138
16138
  const Interactive2 = {
16139
- MovablePoint: MovablePoint$5,
16139
+ MovablePoint: MovablePoint$4,
16140
16140
  addMovablePoint: function (graphie, options) {
16141
16141
  const movable = new Movable(graphie, {});
16142
- return new MovablePoint$5(graphie, movable, options);
16142
+ return new MovablePoint$4(graphie, movable, options);
16143
16143
  },
16144
- MovableLine: MovableLine$3,
16144
+ MovableLine: MovableLine$4,
16145
16145
  addMovableLine: function (graphie, options) {
16146
16146
  const movable = new Movable(graphie, {});
16147
- return new MovableLine$3(graphie, movable, options);
16147
+ return new MovableLine$4(graphie, movable, options);
16148
16148
  },
16149
16149
  MovablePolygon: MovablePolygon,
16150
16150
  addMovablePolygon: function (graphie, options) {
@@ -16181,7 +16181,7 @@ const Interactive2 = {
16181
16181
  }
16182
16182
  };
16183
16183
 
16184
- const MovablePoint$4 = GraphieClasses.createClass({
16184
+ const MovablePoint$3 = GraphieClasses.createClass({
16185
16185
  displayName: "MovablePoint",
16186
16186
  movableProps: ["children"],
16187
16187
  _getProps: function () {
@@ -16239,8 +16239,8 @@ const MovablePoint$4 = GraphieClasses.createClass({
16239
16239
  });
16240
16240
 
16241
16241
  // Include helper methods, such as MovablePoint.constrain.snap()
16242
- ___default["default"].extend(MovablePoint$4, Interactive2.MovablePoint);
16243
- const MovableLine$2 = GraphieClasses.createClass({
16242
+ ___default["default"].extend(MovablePoint$3, Interactive2.MovablePoint);
16243
+ const MovableLine$3 = GraphieClasses.createClass({
16244
16244
  displayName: "MovableLine",
16245
16245
  movableProps: ["children"],
16246
16246
  add: function (graphie) {
@@ -16268,7 +16268,7 @@ const MovableLine$2 = GraphieClasses.createClass({
16268
16268
  });
16269
16269
 
16270
16270
  // Include helper methods, such as MovableLine.constrain.snap()
16271
- ___default["default"].extend(MovableLine$2, Interactive2.MovableLine);
16271
+ ___default["default"].extend(MovableLine$3, Interactive2.MovableLine);
16272
16272
  const Label$1 = GraphieClasses.createSimpleClass((graphie, props) => {
16273
16273
  let coord = props.coord;
16274
16274
  if (props.unscaled) {
@@ -16408,8 +16408,8 @@ var Movables = {
16408
16408
  Circle: Circle,
16409
16409
  Label: Label$1,
16410
16410
  Line: Line$2,
16411
- MovableLine: MovableLine$2,
16412
- MovablePoint: MovablePoint$4,
16411
+ MovableLine: MovableLine$3,
16412
+ MovablePoint: MovablePoint$3,
16413
16413
  Parabola: Parabola,
16414
16414
  Path: Path,
16415
16415
  Plot: Plot$2,
@@ -24979,6 +24979,12 @@ var Dropdown$1 = {
24979
24979
 
24980
24980
  // transform = _.identity
24981
24981
 
24982
+ function mediaQueryIsMatched(mediaQuery) {
24983
+ if (typeof window.matchMedia !== "function") {
24984
+ return false;
24985
+ }
24986
+ return window.matchMedia(mediaQuery).matches;
24987
+ }
24982
24988
  class Explanation extends React__namespace.Component {
24983
24989
  static defaultProps = {
24984
24990
  showPrompt: "Explain",
@@ -25016,7 +25022,7 @@ class Explanation extends React__namespace.Component {
25016
25022
  render() {
25017
25023
  const promptText = this.state.expanded ? this.props.hidePrompt : this.props.showPrompt;
25018
25024
  const caretIcon = this.state.expanded ? caretUp__default["default"] : caretDown__default["default"];
25019
- const allowTransition = window.matchMedia("(prefers-reduced-motion: no-preference)").matches;
25025
+ const allowTransition = mediaQueryIsMatched("(prefers-reduced-motion: no-preference)");
25020
25026
 
25021
25027
  // Special styling is needed to fit the button in a block of text without throwing off the line spacing.
25022
25028
  // While the button is not normally included in a block of text, it needs to be able to accommodate such a case.
@@ -25027,7 +25033,7 @@ class Explanation extends React__namespace.Component {
25027
25033
  };
25028
25034
  const contentStyling = [styles$p.content, this.state.expanded ? styles$p.contentExpanded : styles$p.contentCollapsed, allowTransition && (this.state.expanded ? styles$p.transitionExpanded : styles$p.transitionCollapsed)];
25029
25035
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.UniqueIDProvider, {
25030
- mockOnFirstRender: false,
25036
+ mockOnFirstRender: true,
25031
25037
  scope: "explanation-widget"
25032
25038
  }, ids => /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
25033
25039
  "aria-expanded": this.state.expanded,
@@ -26424,9 +26430,9 @@ var util = /*#__PURE__*/Object.freeze({
26424
26430
  });
26425
26431
 
26426
26432
  // @ts-expect-error - TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'.
26427
- const MovablePoint$3 = Graphie.MovablePoint;
26433
+ const MovablePoint$2 = Graphie.MovablePoint;
26428
26434
  // @ts-expect-error - TS2339 - Property 'MovableLine' does not exist on type 'typeof Graphie'.
26429
- const MovableLine$1 = Graphie.MovableLine;
26435
+ const MovableLine$2 = Graphie.MovableLine;
26430
26436
  function isFlipped(newCoord, oldCoord, line) {
26431
26437
  const CCW = (a, b, c) => {
26432
26438
  return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
@@ -26470,7 +26476,7 @@ class FunctionGrapher extends React__namespace.Component {
26470
26476
  })();
26471
26477
  render() {
26472
26478
  const pointForCoord = (coord, i) => {
26473
- return /*#__PURE__*/React__namespace.createElement(MovablePoint$3, {
26479
+ return /*#__PURE__*/React__namespace.createElement(MovablePoint$2, {
26474
26480
  key: i,
26475
26481
  coord: coord,
26476
26482
  static: this.props.static,
@@ -26582,7 +26588,7 @@ class FunctionGrapher extends React__namespace.Component {
26582
26588
  const dashed = {
26583
26589
  strokeDasharray: "- "
26584
26590
  };
26585
- return asymptote && /*#__PURE__*/React__namespace.createElement(MovableLine$1, {
26591
+ return asymptote && /*#__PURE__*/React__namespace.createElement(MovableLine$2, {
26586
26592
  onMove: (newCoord, oldCoord) => {
26587
26593
  // Calculate and apply displacement
26588
26594
  const delta = kmath.vector.subtract(newCoord, oldCoord);
@@ -26607,7 +26613,7 @@ class FunctionGrapher extends React__namespace.Component {
26607
26613
  }],
26608
26614
  normalStyle: dashed,
26609
26615
  highlightStyle: dashed
26610
- }, ___default["default"].map(asymptote, (coord, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$3, {
26616
+ }, ___default["default"].map(asymptote, (coord, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$2, {
26611
26617
  key: `asymptoteCoord-${i}`,
26612
26618
  coord: coord,
26613
26619
  static: true,
@@ -27370,9 +27376,9 @@ const Label = Graphie.Label;
27370
27376
  // @ts-expect-error - TS2339 - Property 'Line' does not exist on type 'typeof Graphie'.
27371
27377
  const Line$1 = Graphie.Line;
27372
27378
  // @ts-expect-error - TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'.
27373
- const MovablePoint$2 = Graphie.MovablePoint;
27379
+ const MovablePoint$1 = Graphie.MovablePoint;
27374
27380
  // @ts-expect-error - TS2339 - Property 'MovableLine' does not exist on type 'typeof Graphie'.
27375
- const MovableLine = Graphie.MovableLine;
27381
+ const MovableLine$1 = Graphie.MovableLine;
27376
27382
  // @ts-expect-error - TS2339 - Property 'Plot' does not exist on type 'typeof Graphie'.
27377
27383
  const Plot = Graphie.Plot;
27378
27384
  // @ts-expect-error - TS2339 - Property 'PlotParametric' does not exist on type 'typeof Graphie'.
@@ -27624,7 +27630,7 @@ class Interaction extends React__namespace.Component {
27624
27630
  return [coordX, coordY];
27625
27631
  }];
27626
27632
  if (element.options.constraint === "snap") {
27627
- constraints.push(MovablePoint$2.constraints.snap(element.options.snap));
27633
+ constraints.push(MovablePoint$1.constraints.snap(element.options.snap));
27628
27634
  } else if (element.options.constraint === "x") {
27629
27635
  constraints.push(coord => {
27630
27636
  return [
@@ -27646,7 +27652,7 @@ class Interaction extends React__namespace.Component {
27646
27652
  // TODO(eater): foo_[xyz] are hacky non-props to
27647
27653
  // get the component to update when constraints
27648
27654
  // change
27649
- return /*#__PURE__*/React__namespace.createElement(MovablePoint$2, {
27655
+ return /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
27650
27656
  key: element.key,
27651
27657
  coord: [
27652
27658
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
@@ -27681,7 +27687,7 @@ class Interaction extends React__namespace.Component {
27681
27687
  return [coordX, coordY];
27682
27688
  }];
27683
27689
  if (element.options.constraint === "snap") {
27684
- constraints.push(MovablePoint$2.constraints.snap(element.options.snap));
27690
+ constraints.push(MovablePoint$1.constraints.snap(element.options.snap));
27685
27691
  } else if (element.options.constraint === "x") {
27686
27692
  constraints.push(coord => {
27687
27693
  return [
@@ -27709,7 +27715,7 @@ class Interaction extends React__namespace.Component {
27709
27715
  this.state.variables["x_" + element.options.endSubscript],
27710
27716
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
27711
27717
  this.state.variables["y_" + element.options.endSubscript]];
27712
- return /*#__PURE__*/React__namespace.createElement(MovableLine, {
27718
+ return /*#__PURE__*/React__namespace.createElement(MovableLine$1, {
27713
27719
  key: element.key,
27714
27720
  constraints: constraints
27715
27721
  // eslint-disable-next-line react/jsx-no-bind
@@ -27722,14 +27728,14 @@ class Interaction extends React__namespace.Component {
27722
27728
  foo_x: element.options.constraint,
27723
27729
  foo_y: element.options.constraintFn,
27724
27730
  foo_z: element.options.snap
27725
- }, /*#__PURE__*/React__namespace.createElement(MovablePoint$2, {
27731
+ }, /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
27726
27732
  coord: start,
27727
27733
  static: true,
27728
27734
  normalStyle: {
27729
27735
  stroke: "none",
27730
27736
  fill: "none"
27731
27737
  }
27732
- }), /*#__PURE__*/React__namespace.createElement(MovablePoint$2, {
27738
+ }), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
27733
27739
  coord: end,
27734
27740
  static: true,
27735
27741
  normalStyle: {
@@ -28310,20 +28316,142 @@ function moveControlPoint(objectIndex, pointIndex, destination) {
28310
28316
  destination
28311
28317
  };
28312
28318
  }
28313
- const MOVE_SEGMENT = "move-segment";
28314
- function moveSegment(segmentIndex, delta) {
28319
+ const MOVE_LINE = "move-line";
28320
+ function moveSegment(lineIndex, delta) {
28315
28321
  return {
28316
- type: MOVE_SEGMENT,
28317
- segmentIndex,
28322
+ type: MOVE_LINE,
28323
+ lineIndex,
28318
28324
  delta
28319
28325
  };
28320
28326
  }
28321
28327
 
28322
- const MovablePoint$1 = props => {
28328
+ const defaultStroke = "var(--movable-line-stroke-color)";
28329
+ const MovableLine = props => {
28330
+ const {
28331
+ start,
28332
+ end,
28333
+ onMove,
28334
+ extend,
28335
+ stroke = defaultStroke
28336
+ } = props;
28337
+ const midpoint = mafs.vec.midpoint(start, end);
28338
+ const {
28339
+ viewTransform,
28340
+ userTransform
28341
+ } = mafs.useTransformContext();
28342
+ const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
28343
+ const startPtPx = mafs.vec.transform(start, transformToPx);
28344
+ const endPtPx = mafs.vec.transform(end, transformToPx);
28345
+ let startExtend = undefined;
28346
+ let endExtend = undefined;
28347
+ if (extend) {
28348
+ startExtend = extend.start ? getExtensionCoords(end, start, extend.range) : undefined;
28349
+ endExtend = extend.end ? getExtensionCoords(start, end, extend.range) : undefined;
28350
+ }
28351
+ const line = React.useRef(null);
28352
+ const {
28353
+ dragging
28354
+ } = mafs.useMovable({
28355
+ gestureTarget: line,
28356
+ point: midpoint,
28357
+ onMove: newPoint => {
28358
+ onMove(mafs.vec.sub(newPoint, midpoint));
28359
+ },
28360
+ constrain: p => p
28361
+ });
28362
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("g", {
28363
+ ref: line,
28364
+ tabIndex: 0,
28365
+ className: "movable-line",
28366
+ style: {
28367
+ cursor: dragging ? "grabbing" : "grab"
28368
+ }
28369
+ }, /*#__PURE__*/React__namespace.createElement(SVGLine, {
28370
+ start: startPtPx,
28371
+ end: endPtPx,
28372
+ style: {
28373
+ stroke: "transparent",
28374
+ strokeWidth: 44
28375
+ }
28376
+ }), /*#__PURE__*/React__namespace.createElement(SVGLine, {
28377
+ start: startPtPx,
28378
+ end: endPtPx,
28379
+ style: {
28380
+ stroke,
28381
+ strokeWidth: "var(--movable-line-stroke-weight)"
28382
+ },
28383
+ dragging: dragging
28384
+ })), startExtend && /*#__PURE__*/React__namespace.createElement(mafs.Vector, {
28385
+ tail: start,
28386
+ tip: startExtend,
28387
+ color: stroke
28388
+ }), endExtend && /*#__PURE__*/React__namespace.createElement(mafs.Vector, {
28389
+ tail: end,
28390
+ tip: endExtend,
28391
+ color: stroke
28392
+ }));
28393
+ };
28394
+ function SVGLine(props) {
28395
+ const {
28396
+ start,
28397
+ end,
28398
+ style,
28399
+ dragging
28400
+ } = props;
28401
+ return /*#__PURE__*/React__namespace.createElement("line", {
28402
+ x1: start[0],
28403
+ y1: start[1],
28404
+ x2: end[0],
28405
+ y2: end[1],
28406
+ style: style,
28407
+ className: dragging ? "movable-line-dragging" : undefined
28408
+ });
28409
+ }
28410
+
28411
+ /**
28412
+ * Given two points, find the tips that extends through the points to the edge of the range.
28413
+ * @param collinearPoint - The point that the line passes through. Needed to establish slope.
28414
+ * @param extendFrom - The point that the line extends from to the edge of the graph.
28415
+ */
28416
+ const getExtensionCoords = (collinearPoint, extendFrom, range) => {
28417
+ // edges of the graph
28418
+ const [[xMin, xMax], [yMin, yMax]] = range;
28419
+ const [aX, aY] = collinearPoint;
28420
+ const [bX, bY] = extendFrom;
28421
+ const yDiff = bY - aY;
28422
+ const xDiff = bX - aX;
28423
+ const slope = yDiff / xDiff;
28424
+ const yAtXMin = slope * (xMin - aX) + aY;
28425
+ const yAtXMax = slope * (xMax - aX) + aY;
28426
+ const xAtYMin = (yMin - aY) / slope + aX;
28427
+ const xAtYMax = (yMax - aY) / slope + aX;
28428
+
28429
+ // clock analogy to describe quadrants
28430
+ switch (true) {
28431
+ // 12 o'clock to 2:59
28432
+ case yDiff > 0 && xDiff >= 0:
28433
+ return xAtYMax > xMax ? [xMax, yAtXMax] : [xAtYMax, yMax];
28434
+ // 3 o'clock to 5:59
28435
+ case yDiff <= 0 && xDiff > 0:
28436
+ // xAtYMin evaluates to -Infinity here, so we use absolute value
28437
+ return Math.abs(xAtYMin) > xMax ? [xMax, yAtXMax] : [xAtYMin, yMin];
28438
+ // 9 o'clock to 11:59
28439
+ case yDiff >= 0 && xDiff < 0:
28440
+ return xAtYMax < xMin ? [xMin, yAtXMin] : [xAtYMax, yMax];
28441
+ // 6 o'clock to 8:59
28442
+ case yDiff < 0 && xDiff <= 0:
28443
+ return xAtYMin < xMin ? [xMin, yAtXMin] : [xAtYMin, yMin];
28444
+ default:
28445
+ return [xMax, yAtXMax];
28446
+ }
28447
+ };
28448
+
28449
+ const StyledMovablePoint = props => {
28323
28450
  const hitboxRef = React.useRef(null);
28324
28451
  const {
28325
28452
  point,
28326
- onMove
28453
+ onMove,
28454
+ color = Color__default["default"].blue
28327
28455
  } = props;
28328
28456
  const {
28329
28457
  dragging
@@ -28347,11 +28475,11 @@ const MovablePoint$1 = props => {
28347
28475
  cursor: dragging ? "grabbing" : "grab",
28348
28476
  touchAction: "none",
28349
28477
  outline: "none",
28350
- "--movable-point-color": Color__default["default"].blue
28478
+ "--movable-point-color": color
28351
28479
  }
28352
28480
  }, /*#__PURE__*/React__namespace.createElement("circle", {
28353
28481
  className: "movable-point-hitbox",
28354
- r: 30,
28482
+ r: 22,
28355
28483
  cx: x,
28356
28484
  cy: y
28357
28485
  }), /*#__PURE__*/React__namespace.createElement("circle", {
@@ -28367,7 +28495,7 @@ const MovablePoint$1 = props => {
28367
28495
  cx: x,
28368
28496
  cy: y,
28369
28497
  style: {
28370
- fill: Color__default["default"].blue
28498
+ fill: color
28371
28499
  }
28372
28500
  }));
28373
28501
  };
@@ -28377,16 +28505,16 @@ const SegmentGraph = props => {
28377
28505
  dispatch
28378
28506
  } = props;
28379
28507
  const {
28380
- segments,
28508
+ coords: segments,
28381
28509
  snapStep,
28382
28510
  range
28383
28511
  } = props.graphState;
28384
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, segments.map((segment, i) => /*#__PURE__*/React__namespace.createElement(SegmentView, {
28512
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, segments?.map((segment, i) => /*#__PURE__*/React__namespace.createElement(SegmentView, {
28385
28513
  key: i,
28386
- segment: segment,
28514
+ collinearPair: segment,
28387
28515
  snaps: snapStep,
28388
28516
  range: range,
28389
- onMoveSegment: delta => {
28517
+ onMoveLine: delta => {
28390
28518
  dispatch(moveSegment(i, delta));
28391
28519
  },
28392
28520
  onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(i, endpointIndex, destination)),
@@ -28395,75 +28523,115 @@ const SegmentGraph = props => {
28395
28523
  };
28396
28524
  const SegmentView = props => {
28397
28525
  const {
28398
- onMoveSegment,
28399
- segment: [pt1, pt2]
28526
+ onMoveLine: onMoveSegment,
28527
+ collinearPair: [start, end]
28400
28528
  } = props;
28401
- const midpoint = mafs.vec.midpoint(pt1, pt2);
28402
- const segment = React.useRef(null);
28403
- const {
28404
- dragging: isDraggingSegment
28405
- } = mafs.useMovable({
28406
- gestureTarget: segment,
28407
- point: midpoint,
28408
- onMove: newPoint => {
28409
- onMoveSegment(mafs.vec.sub(newPoint, midpoint));
28410
- },
28411
- constrain: p => p
28412
- });
28413
- const {
28414
- viewTransform,
28415
- userTransform
28416
- } = mafs.useTransformContext();
28417
- const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
28418
- const pt1Px = mafs.vec.transform(pt1, transformToPx);
28419
- const pt2Px = mafs.vec.transform(pt2, transformToPx);
28420
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("g", {
28421
- ref: segment,
28422
- tabIndex: 0,
28423
- className: "movable-segment",
28424
- style: {
28425
- cursor: isDraggingSegment ? "grabbing" : "grab"
28426
- }
28427
- }, /*#__PURE__*/React__namespace.createElement(SVGLine, {
28428
- start: pt1Px,
28429
- end: pt2Px,
28430
- style: {
28431
- stroke: "transparent",
28432
- strokeWidth: 30
28433
- }
28434
- }), /*#__PURE__*/React__namespace.createElement(SVGLine, {
28435
- start: pt1Px,
28436
- end: pt2Px,
28437
- style: {
28438
- stroke: "var(--mafs-segment-stroke-color)",
28439
- strokeWidth: "var(--mafs-segment-stroke-weight)"
28440
- }
28441
- })), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
28442
- point: pt1,
28529
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(MovableLine, {
28530
+ start: start,
28531
+ end: end,
28532
+ onMove: onMoveSegment
28533
+ }), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
28534
+ point: start,
28443
28535
  onMove: newPoint => {
28444
28536
  props.onMovePoint(0, newPoint);
28445
28537
  }
28446
- }), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
28447
- point: pt2,
28538
+ }), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
28539
+ point: end,
28448
28540
  onMove: newPoint => {
28449
28541
  props.onMovePoint(1, newPoint);
28450
28542
  }
28451
28543
  }));
28452
28544
  };
28453
- function SVGLine(props) {
28545
+
28546
+ const LinearGraph = props => {
28454
28547
  const {
28455
- start,
28456
- end,
28457
- style
28548
+ dispatch
28458
28549
  } = props;
28459
- return /*#__PURE__*/React__namespace.createElement("line", {
28460
- x1: start[0],
28461
- y1: start[1],
28462
- x2: end[0],
28463
- y2: end[1],
28464
- style: style
28465
- });
28466
- }
28550
+ const {
28551
+ coords: lines,
28552
+ snapStep,
28553
+ range,
28554
+ type
28555
+ } = props.graphState;
28556
+ const colors = ["var(--movable-line-stroke-color)", "var(--mafs-violet)"];
28557
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, lines?.map((line, i) => /*#__PURE__*/React__namespace.createElement(LineView, {
28558
+ key: i,
28559
+ collinearPair: line,
28560
+ snaps: snapStep,
28561
+ range: range,
28562
+ onMoveLine: delta => {
28563
+ dispatch(moveSegment(i, delta));
28564
+ },
28565
+ onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(i, endpointIndex, destination))
28566
+ // "linear" or "linear-system" + index
28567
+ ,
28568
+ "data-testid": type + i,
28569
+ stroke: colors[i]
28570
+ })));
28571
+ };
28572
+ const LineView = props => {
28573
+ const {
28574
+ onMoveLine,
28575
+ onMovePoint,
28576
+ collinearPair: [start, end],
28577
+ range,
28578
+ stroke
28579
+ } = props;
28580
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(MovableLine, {
28581
+ start: start,
28582
+ end: end,
28583
+ onMove: onMoveLine,
28584
+ extend: {
28585
+ start: true,
28586
+ end: true,
28587
+ range
28588
+ },
28589
+ stroke: stroke
28590
+ }), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
28591
+ point: start,
28592
+ onMove: newPoint => {
28593
+ onMovePoint(0, newPoint);
28594
+ },
28595
+ color: stroke
28596
+ }), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
28597
+ point: end,
28598
+ onMove: newPoint => {
28599
+ onMovePoint(1, newPoint);
28600
+ },
28601
+ color: stroke
28602
+ }));
28603
+ };
28604
+
28605
+ const RayGraph = props => {
28606
+ const {
28607
+ dispatch
28608
+ } = props;
28609
+ const {
28610
+ coords: lines,
28611
+ range
28612
+ } = props.graphState;
28613
+
28614
+ // a ray only has one line
28615
+ const [start, end] = lines[0];
28616
+ const handleMoveLine = delta => dispatch(moveSegment(0, delta));
28617
+ const handleMovePoint = (newPoint, pointIndex) => dispatch(moveControlPoint(0, pointIndex, newPoint));
28618
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(MovableLine, {
28619
+ start: start,
28620
+ end: end,
28621
+ onMove: handleMoveLine,
28622
+ extend: {
28623
+ end: true,
28624
+ start: false,
28625
+ range
28626
+ }
28627
+ }), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
28628
+ point: start,
28629
+ onMove: newPoint => handleMovePoint(newPoint, 0)
28630
+ }), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
28631
+ point: end,
28632
+ onMove: newPoint => handleMovePoint(newPoint, 1)
28633
+ }));
28634
+ };
28467
28635
 
28468
28636
  const renderLineLabel = (n, _ref) => {
28469
28637
  let [min, max] = _ref;
@@ -28482,35 +28650,50 @@ const Grid = props => props.markings === "none" ? null : /*#__PURE__*/React__nam
28482
28650
 
28483
28651
  function interactiveGraphReducer(state, action) {
28484
28652
  switch (action.type) {
28485
- case "move-control-point":
28653
+ case MOVE_CONTROL_POINT:
28486
28654
  {
28487
- const newSegments = updateAtIndex(state.segments, action.objectIndex, segment => setAtIndex(segment, action.pointIndex, snap(state, bound$1(state, action.destination))));
28488
- if (!validSegments(newSegments)) {
28655
+ const newCoords = updateAtIndex({
28656
+ array: state.coords,
28657
+ index: action.objectIndex,
28658
+ update: tuple => setAtIndex({
28659
+ array: tuple,
28660
+ index: action.pointIndex,
28661
+ newValue: snap(state, bound$1(state, action.destination))
28662
+ })
28663
+ });
28664
+ if (!validSegments(newCoords)) {
28489
28665
  return state;
28490
28666
  }
28491
28667
  return {
28492
28668
  ...state,
28493
28669
  hasBeenInteractedWith: true,
28494
- segments: newSegments
28670
+ coords: newCoords
28495
28671
  };
28496
28672
  }
28497
- case "move-segment":
28673
+ case MOVE_LINE:
28498
28674
  {
28499
- const oldSegment = state.segments[action.segmentIndex];
28500
- const maxMoves = oldSegment.map(point => maxMove(state, point));
28501
- const minMoves = oldSegment.map(point => minMove(state, point));
28675
+ const currentLine = state.coords?.[action.lineIndex];
28676
+ if (!currentLine) {
28677
+ throw new Error("No line to move");
28678
+ }
28679
+ const maxMoves = currentLine.map(point => maxMove(state, point));
28680
+ const minMoves = currentLine.map(point => minMove(state, point));
28502
28681
  const maxXMove = Math.min(...maxMoves.map(move => move[0]));
28503
28682
  const maxYMove = Math.min(...maxMoves.map(move => move[1]));
28504
28683
  const minXMove = Math.max(...minMoves.map(move => move[0]));
28505
28684
  const minYMove = Math.max(...minMoves.map(move => move[1]));
28506
28685
  const dx = clamp(action.delta[0], minXMove, maxXMove);
28507
28686
  const dy = clamp(action.delta[1], minYMove, maxYMove);
28508
- const newSegment = oldSegment.map(point => snap(state, kmath.vector.add(point, [dx, dy])));
28509
- const newSegments = setAtIndex(state.segments, action.segmentIndex, newSegment);
28687
+ const newValue = [snap(state, mafs.vec.add(currentLine[0], [dx, dy])), snap(state, mafs.vec.add(currentLine[1], [dx, dy]))];
28688
+ const newLine = setAtIndex({
28689
+ array: state.coords,
28690
+ index: action.lineIndex,
28691
+ newValue
28692
+ });
28510
28693
  return {
28511
28694
  ...state,
28512
28695
  hasBeenInteractedWith: true,
28513
- segments: newSegments
28696
+ coords: newLine
28514
28697
  };
28515
28698
  }
28516
28699
  default:
@@ -28559,12 +28742,28 @@ function validSegments(segments) {
28559
28742
  return !kmath.vector.equal(start, end);
28560
28743
  });
28561
28744
  }
28562
- function updateAtIndex(array, index, update) {
28563
- return setAtIndex(array, index, update(array[index]));
28745
+ function updateAtIndex(args) {
28746
+ const {
28747
+ array = [],
28748
+ index,
28749
+ update
28750
+ } = args;
28751
+ const newValue = update(array[index]);
28752
+ return setAtIndex({
28753
+ array,
28754
+ index,
28755
+ newValue
28756
+ });
28564
28757
  }
28565
- function setAtIndex(array, index, newValue) {
28758
+ function setAtIndex(args) {
28759
+ const {
28760
+ array = [],
28761
+ index,
28762
+ newValue
28763
+ } = args;
28566
28764
  const copy = [...array];
28567
28765
  copy[index] = newValue;
28766
+ // restoring readonly to array
28568
28767
  return copy;
28569
28768
  }
28570
28769
 
@@ -28602,12 +28801,24 @@ function initializeGraphState(params) {
28602
28801
  hasBeenInteractedWith: false,
28603
28802
  range,
28604
28803
  snapStep,
28605
- segments: getDefaultSegments({
28804
+ coords: getDefaultSegments({
28606
28805
  graph,
28607
28806
  step,
28608
28807
  range
28609
28808
  })
28610
28809
  };
28810
+ case "linear":
28811
+ case "linear-system":
28812
+ case "ray":
28813
+ return {
28814
+ type: graph.type,
28815
+ hasBeenInteractedWith: false,
28816
+ range,
28817
+ snapStep,
28818
+ // Linear and ray graphs have a single tuple of points, while a
28819
+ // linear system has two tuples of points.
28820
+ coords: getLineCoords(graph, range, step)
28821
+ };
28611
28822
  }
28612
28823
  throw new Error("Mafs not yet implemented for graph type: " + graph.type);
28613
28824
  }
@@ -28636,6 +28847,35 @@ const getDefaultSegments = props => {
28636
28847
  });
28637
28848
  };
28638
28849
 
28850
+ // TS v4 doesn't narrow return types, while v5 does.
28851
+ // Instead of updating to v5, using generic type to relate input and output types.
28852
+ function getGradableGraph(state, initialGraph) {
28853
+ if (!state.hasBeenInteractedWith) {
28854
+ return initialGraph;
28855
+ }
28856
+ switch (true) {
28857
+ // coords: Array of CollinearTuple
28858
+ case state.type === "linear-system" && initialGraph.type === "linear-system":
28859
+ case state.type === "segment" && initialGraph.type === "segment":
28860
+ return {
28861
+ ...initialGraph,
28862
+ coords: state.coords
28863
+ };
28864
+ // coords: CollinearTuple
28865
+ case state.type === "linear" && initialGraph.type === "linear":
28866
+ case state.type === "ray" && initialGraph.type === "ray":
28867
+ return {
28868
+ ...initialGraph,
28869
+ coords: state.coords?.[0]
28870
+ };
28871
+ }
28872
+ throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
28873
+ }
28874
+ const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
28875
+ const getLineCoords = (graph, range, step) =>
28876
+ // Return two lines for a linear system, one for a ray or linear
28877
+ graph.coords ?? graph.type === "linear-system" ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])];
28878
+
28639
28879
  // Displays a stylized open eye: lint warnings are visible
28640
28880
  const VisibleIcon = () => /*#__PURE__*/React__namespace.createElement("svg", {
28641
28881
  width: "24",
@@ -29333,28 +29573,30 @@ const renderGraph = props => {
29333
29573
  state,
29334
29574
  dispatch
29335
29575
  } = props;
29336
- switch (state.type) {
29576
+ const {
29577
+ type
29578
+ } = state;
29579
+ switch (type) {
29337
29580
  case "segment":
29338
29581
  return /*#__PURE__*/React__namespace.createElement(SegmentGraph, {
29339
29582
  graphState: state,
29340
29583
  dispatch: dispatch
29341
29584
  });
29585
+ case "linear":
29586
+ case "linear-system":
29587
+ return /*#__PURE__*/React__namespace.createElement(LinearGraph, {
29588
+ graphState: state,
29589
+ dispatch: dispatch
29590
+ });
29591
+ case "ray":
29592
+ return /*#__PURE__*/React__namespace.createElement(RayGraph, {
29593
+ graphState: state,
29594
+ dispatch: dispatch
29595
+ });
29596
+ default:
29597
+ return new wonderStuffCore.UnreachableCaseError(type);
29342
29598
  }
29343
- throw new Error("Mafs is not yet implemented for graph type: " + state.type);
29344
29599
  };
29345
- function getGradableGraph(state, initialGraph) {
29346
- if (!state.hasBeenInteractedWith) {
29347
- return initialGraph;
29348
- }
29349
- switch (initialGraph.type) {
29350
- case "segment":
29351
- return {
29352
- ...initialGraph,
29353
- coords: state.segments
29354
- };
29355
- }
29356
- throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
29357
- }
29358
29600
  const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
29359
29601
  const [width, height] = props.box;
29360
29602
  const legacyGrid = getLegacyGrid([width, height], props.backgroundImage);
@@ -41363,7 +41605,7 @@ const init = function (options) {
41363
41605
 
41364
41606
  // This file is processed by a Rollup plugin (replace) to inject the production
41365
41607
  const libName = "@khanacademy/perseus";
41366
- const libVersion = "20.3.1";
41608
+ const libVersion = "20.4.1";
41367
41609
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
41368
41610
 
41369
41611
  const apiVersion = {
@@ -42393,8 +42635,9 @@ var constants = /*#__PURE__*/Object.freeze({
42393
42635
  // TODO(FEI-4010): Remove `Perseus` prefix for all types here
42394
42636
  // TODO(FEI-4011): Use types generated by https://github.com/jaredly/generate-perseus-flowtypes
42395
42637
 
42638
+ // Range is replaced within this file with Interval, but it is used elsewhere
42639
+ // and exported from the package, so we need to keep it around.
42396
42640
  // TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
42397
-
42398
42641
  const ItemExtras = [
42399
42642
  // The user might benefit from using a Scientific Calculator. Provided on Khan Academy when true
42400
42643
  "calculator",