@khanacademy/perseus 24.0.0 → 24.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/es/index.css CHANGED
@@ -3518,7 +3518,7 @@ body.mobile .framework-perseus table.perseus-widget-table-of-values.non-markdown
3518
3518
  font-size: 18px;
3519
3519
  }
3520
3520
  }
3521
- .perseus-widget-editor-content .locked-figure-accordion h2 {
3521
+ .perseus-widget-editor-content .perseus-editor-accordion h2 {
3522
3522
  padding-top: 0;
3523
3523
  }
3524
3524
  .keypad-input {
@@ -4143,6 +4143,34 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4143
4143
  }
4144
4144
 
4145
4145
 
4146
+ .MafsView .protractor-rotation-handle {
4147
+ cursor: grab;
4148
+ /* `pointer-events: all` is needed to make the hit target the size of the
4149
+ * entire <g> element and not just its visible subelements */
4150
+ pointer-events: all;
4151
+ transition: transform 0.1s ease-out;
4152
+ }
4153
+
4154
+ .MafsView .protractor-rotation-handle:hover {
4155
+ transform: scale(1.3);
4156
+ }
4157
+
4158
+ .MafsView .protractor-rotation-handle-arrow-arc {
4159
+ fill: none;
4160
+ stroke: var(--mafs-blue);
4161
+ stroke-width: 8;
4162
+ /* `strokeLinecap: square` prevents a hairline crack from appearing
4163
+ * between the arc and the arrowheads, by making the arc extend a bit
4164
+ * beyond its start and end coordinates.
4165
+ */
4166
+ stroke-linecap: square;
4167
+ }
4168
+
4169
+ .MafsView .protractor-rotation-handle-arrowhead {
4170
+ fill: var(--mafs-blue);
4171
+ stroke: none;
4172
+ }
4173
+
4146
4174
  .MafsView {
4147
4175
  display: block;
4148
4176
  background: var(--mafs-bg);
package/dist/es/index.js CHANGED
@@ -29,7 +29,8 @@ 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, Ellipse, Line as Line$4, Point as Point$2, Polygon, useMovable, Text as Text$1, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
32
+ import { vec, Ellipse, Line as Line$4, Point as Point$2, Polygon, useTransformContext, Text as Text$1, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
33
+ import { useDrag } from '@use-gesture/react';
33
34
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
34
35
  import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
35
36
  import questionIcon from '@phosphor-icons/core/regular/question.svg';
@@ -13085,7 +13086,7 @@ _.extend(GraphUtils.Graphie.prototype, {
13085
13086
  }(),
13086
13087
  protractor: function (center) {
13087
13088
  // @ts-expect-error - TS2350 - Only a void function can be called with the 'new' keyword.
13088
- return new Protractor(this, center);
13089
+ return new Protractor$1(this, center);
13089
13090
  },
13090
13091
  ruler: function (options) {
13091
13092
  // @ts-expect-error - TS2350 - Only a void function can be called with the 'new' keyword.
@@ -13093,7 +13094,7 @@ _.extend(GraphUtils.Graphie.prototype, {
13093
13094
  },
13094
13095
  addPoints: vector$1.add
13095
13096
  });
13096
- function Protractor(graph, center) {
13097
+ function Protractor$1(graph, center) {
13097
13098
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
13098
13099
  this.set = graph.raphael.set();
13099
13100
 
@@ -28557,7 +28558,24 @@ class PathBuilder {
28557
28558
  move(x, y) {
28558
28559
  this.path.push({
28559
28560
  action: "M",
28560
- coords: [x, y]
28561
+ args: [x, y]
28562
+ });
28563
+ return this;
28564
+ }
28565
+ line(x, y) {
28566
+ this.path.push({
28567
+ action: "L",
28568
+ args: [x, y]
28569
+ });
28570
+ return this;
28571
+ }
28572
+ circularArc(radius, toX, toY, {
28573
+ sweep = false,
28574
+ largeArc = false
28575
+ } = {}) {
28576
+ this.path.push({
28577
+ action: "A",
28578
+ args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
28561
28579
  });
28562
28580
  return this;
28563
28581
  }
@@ -28566,7 +28584,7 @@ class PathBuilder {
28566
28584
  curve(control1X, control1Y, control2X, control2Y, endX, endY) {
28567
28585
  this.path.push({
28568
28586
  action: "C",
28569
- coords: [control1X, control1Y, control2X, control2Y, endX, endY]
28587
+ args: [control1X, control1Y, control2X, control2Y, endX, endY]
28570
28588
  });
28571
28589
  return this;
28572
28590
  }
@@ -28578,12 +28596,38 @@ class PathBuilder {
28578
28596
  }
28579
28597
  }
28580
28598
  function commandToString(command) {
28581
- return `${command.action}${command.coords.join(" ")}`;
28599
+ return `${command.action}${command.args.join(" ")}`;
28582
28600
  }
28583
28601
  function scaleCommandBy(scaleFactor) {
28584
- return command => _extends({}, command, {
28585
- coords: command.coords.map(c => c * scaleFactor)
28586
- });
28602
+ return command => {
28603
+ switch (command.action) {
28604
+ case "A":
28605
+ // TODO(benchristel): do we want to refactor this switch to
28606
+ // use polymorphism?
28607
+ // Arc command
28608
+ return _extends({}, command, {
28609
+ args: [command.args[0] * scaleFactor,
28610
+ // x radius
28611
+ command.args[1] * scaleFactor,
28612
+ // y radius
28613
+ command.args[2],
28614
+ // rotation
28615
+ command.args[3],
28616
+ // largeArc flag
28617
+ command.args[4],
28618
+ // sweep flag
28619
+ command.args[5] * scaleFactor,
28620
+ // end x
28621
+ command.args[6] * scaleFactor // end y
28622
+ ]
28623
+ });
28624
+
28625
+ default:
28626
+ return _extends({}, command, {
28627
+ args: command.args.map(c => c * scaleFactor)
28628
+ });
28629
+ }
28630
+ };
28587
28631
  }
28588
28632
 
28589
28633
  // We use the pathBuilder here to scale up the SVG path coordinates used
@@ -28971,8 +29015,169 @@ function snap(snapStep, point) {
28971
29015
  return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
28972
29016
  }
28973
29017
 
28974
- function useDraggable(params) {
28975
- return useMovable(params);
29018
+ // Returns the closest point to the given `point` that is within the graph
29019
+ // bounds given in `state`.
29020
+ function bound$1({
29021
+ snapStep,
29022
+ range,
29023
+ point
29024
+ }) {
29025
+ const [requestedX, requestedY] = point;
29026
+ const [snapX, snapY] = snapStep;
29027
+ const [[minX, maxX], [minY, maxY]] = range;
29028
+ return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
29029
+ }
29030
+ function clamp(value, min, max) {
29031
+ if (value < min) {
29032
+ return min;
29033
+ }
29034
+ if (value > max) {
29035
+ return max;
29036
+ }
29037
+ return value;
29038
+ }
29039
+
29040
+ var isProduction = process.env.NODE_ENV === 'production';
29041
+ var prefix = 'Invariant failed';
29042
+ function invariant(condition, message) {
29043
+ if (condition) {
29044
+ return;
29045
+ }
29046
+ if (isProduction) {
29047
+ throw new Error(prefix);
29048
+ }
29049
+ var provided = typeof message === 'function' ? message() : message;
29050
+ var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
29051
+ throw new Error(value);
29052
+ }
29053
+
29054
+ /**
29055
+ * Code in this file is derived from
29056
+ * https://github.com/stevenpetryk/mafs/blob/4520319379a2cc2df8148d8baaef1f85db117103/src/interaction/useMovable.tsx#L20-L83
29057
+ * and copied here under the terms of the MIT license.
29058
+ *
29059
+ * Copyright 2021 Steven Petryk
29060
+ *
29061
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
29062
+ * of this software and associated documentation files (the "Software"), to deal
29063
+ * in the Software without restriction, including without limitation the rights
29064
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29065
+ * copies of the Software, and to permit persons to whom the Software is
29066
+ * furnished to do so, subject to the following conditions:
29067
+ *
29068
+ * The above copyright notice and this permission notice shall be included in
29069
+ * all copies or substantial portions of the Software.
29070
+ */
29071
+
29072
+ function useDraggable(args) {
29073
+ const {
29074
+ gestureTarget: target,
29075
+ onMove,
29076
+ point,
29077
+ constrain
29078
+ } = args;
29079
+ const [dragging, setDragging] = React.useState(false);
29080
+ const {
29081
+ xSpan,
29082
+ ySpan
29083
+ } = useSpanContext();
29084
+ const {
29085
+ viewTransform,
29086
+ userTransform
29087
+ } = useTransformContext();
29088
+ const inverseViewTransform = vec.matrixInvert(viewTransform);
29089
+ invariant(inverseViewTransform, "The view transform must be invertible.");
29090
+ const inverseTransform = React.useMemo(() => getInverseTransform(userTransform), [userTransform]);
29091
+ const pickup = React.useRef([0, 0]);
29092
+ useDrag(state => {
29093
+ const {
29094
+ type,
29095
+ event
29096
+ } = state;
29097
+ event == null || event.stopPropagation();
29098
+ const isKeyboard = type.includes("key");
29099
+ if (isKeyboard) {
29100
+ event == null || event.preventDefault();
29101
+ const {
29102
+ direction: yDownDirection,
29103
+ altKey,
29104
+ metaKey,
29105
+ shiftKey
29106
+ } = state;
29107
+ const direction = [yDownDirection[0], -yDownDirection[1]];
29108
+ const span = Math.abs(direction[0]) ? xSpan : ySpan;
29109
+ let divisions = 50;
29110
+ if (altKey || metaKey) {
29111
+ divisions = 200;
29112
+ }
29113
+ if (shiftKey) {
29114
+ divisions = 10;
29115
+ }
29116
+ const min = span / (divisions * 2);
29117
+ const tests = range(span / divisions, span / 2, span / divisions);
29118
+ for (const dx of tests) {
29119
+ // Transform the test back into the point's coordinate system
29120
+ const testMovement = vec.scale(direction, dx);
29121
+ const testPoint = constrain(vec.transform(vec.add(vec.transform(point, userTransform), testMovement), inverseTransform));
29122
+ if (vec.dist(testPoint, point) > min) {
29123
+ onMove(testPoint);
29124
+ break;
29125
+ }
29126
+ }
29127
+ } else {
29128
+ const {
29129
+ last,
29130
+ movement: pixelMovement,
29131
+ first
29132
+ } = state;
29133
+ setDragging(!last);
29134
+ if (first) {
29135
+ pickup.current = vec.transform(point, userTransform);
29136
+ }
29137
+ if (vec.mag(pixelMovement) === 0) {
29138
+ return;
29139
+ }
29140
+ const movement = vec.transform(pixelMovement, inverseViewTransform);
29141
+ onMove(constrain(vec.transform(vec.add(pickup.current, movement), inverseTransform)));
29142
+ }
29143
+ }, {
29144
+ target,
29145
+ eventOptions: {
29146
+ passive: false
29147
+ }
29148
+ });
29149
+ return {
29150
+ dragging
29151
+ };
29152
+ }
29153
+ function getInverseTransform(transform) {
29154
+ const invert = vec.matrixInvert(transform);
29155
+ invariant(invert !== null, "Could not invert transform matrix. A parent transformation matrix might be degenerative (mapping 2D space to a line).");
29156
+ return invert;
29157
+ }
29158
+ function useSpanContext() {
29159
+ const {
29160
+ range: [[xMin, xMax], [yMin, yMax]]
29161
+ } = useGraphConfig();
29162
+ const xSpan = xMax - xMin;
29163
+ const ySpan = yMax - yMin;
29164
+ return {
29165
+ xSpan,
29166
+ ySpan
29167
+ };
29168
+ }
29169
+ function range(min, max, step = 1) {
29170
+ const result = [];
29171
+ for (let i = min; i < max - step / 2; i += step) {
29172
+ result.push(i);
29173
+ }
29174
+ const computedMax = result[result.length - 1] + step;
29175
+ if (Math.abs(max - computedMax) < step / 1e-6) {
29176
+ result.push(max);
29177
+ } else {
29178
+ result.push(computedMax);
29179
+ }
29180
+ return result;
28976
29181
  }
28977
29182
 
28978
29183
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
@@ -30995,6 +31200,154 @@ const LegacyGrid = ({
30995
31200
  return null;
30996
31201
  };
30997
31202
 
31203
+ const protractorImage = "https://ka-perseus-graphie.s3.amazonaws.com/e9d032f2ab8b95979f674fbfa67056442ba1ff6a.png";
31204
+
31205
+ // The vector from the center of the protractor to the top left corner of the
31206
+ // protractor image, in pixels. Used for positioning.
31207
+ const centerToTopLeft = [-180, -170];
31208
+
31209
+ // The vector from the center of the protractor to the center of the rotation
31210
+ // handle.
31211
+ const centerToRotationHandle = [-176, -15];
31212
+ function Protractor() {
31213
+ const {
31214
+ range,
31215
+ snapStep
31216
+ } = useGraphConfig();
31217
+ const [[xMin, xMax], [yMin, yMax]] = range;
31218
+ // Position the protractor in the center of the graph (horizontally), and
31219
+ // 95% of the way to the bottom of the graph (vertically).
31220
+ const initialCenter = [lerp(xMin, xMax, 0.5), lerp(yMin, yMax, 0.05)];
31221
+ const [center, setCenter] = useState(initialCenter);
31222
+ const [rotationHandleOffset, setRotationHandleOffset] = useState(centerToRotationHandle);
31223
+ const draggableRef = useRef(null);
31224
+ useDraggable({
31225
+ gestureTarget: draggableRef,
31226
+ onMove: setCenter,
31227
+ point: center,
31228
+ constrain: point => bound$1({
31229
+ snapStep,
31230
+ range,
31231
+ point
31232
+ })
31233
+ });
31234
+ const rotationHandleRef = useRef(null);
31235
+ useDraggablePx({
31236
+ gestureTarget: rotationHandleRef,
31237
+ onMove: setRotationHandleOffset,
31238
+ point: rotationHandleOffset,
31239
+ constrain: constrainToCircle
31240
+ });
31241
+ const [centerPx] = useTransformVectorsToPixels(center);
31242
+ const topLeftPx = vec.add(centerPx, centerToTopLeft);
31243
+ const angle = calculateAngleInDegrees(rotationHandleOffset) - calculateAngleInDegrees(centerToRotationHandle);
31244
+ return /*#__PURE__*/React.createElement("g", {
31245
+ ref: draggableRef,
31246
+ transform: `translate(${topLeftPx[0]}, ${topLeftPx[1]}), rotate(${angle})`,
31247
+ style: {
31248
+ transformOrigin: `${-centerToTopLeft[0]}px ${-centerToTopLeft[1]}px`
31249
+ }
31250
+ }, /*#__PURE__*/React.createElement("image", {
31251
+ href: protractorImage
31252
+ }), /*#__PURE__*/React.createElement("g", {
31253
+ transform: `translate(5, ${-centerToTopLeft[1]})`,
31254
+ ref: rotationHandleRef
31255
+ }, /*#__PURE__*/React.createElement(RotationArrow, null)));
31256
+ }
31257
+ function RotationArrow() {
31258
+ const radius = 175;
31259
+ const angleDeg = 10;
31260
+ const angleRad = degreesToRadians(angleDeg);
31261
+ const endX = radius * (1 - Math.cos(angleRad));
31262
+ const endY = radius * -Math.sin(angleRad);
31263
+ const rotationArrow = pathBuilder().move(0, 0).circularArc(radius, endX, endY, {
31264
+ sweep: true
31265
+ }).build();
31266
+ const arrowhead = pathBuilder().move(-8, 0).line(0, 10).line(8, 0).build();
31267
+ const targetRadius = TARGET_SIZE / 2;
31268
+ return /*#__PURE__*/React.createElement("g", {
31269
+ className: "protractor-rotation-handle"
31270
+ }, /*#__PURE__*/React.createElement("path", {
31271
+ className: "protractor-rotation-handle-arrow-arc",
31272
+ d: rotationArrow
31273
+ }), /*#__PURE__*/React.createElement("path", {
31274
+ className: "protractor-rotation-handle-arrowhead",
31275
+ d: arrowhead
31276
+ }), /*#__PURE__*/React.createElement("path", {
31277
+ className: "protractor-rotation-handle-arrowhead",
31278
+ d: arrowhead,
31279
+ transform: `translate(${endX}, ${endY}), rotate(${180 + angleDeg})`
31280
+ }), /*#__PURE__*/React.createElement("ellipse", {
31281
+ cx: "0px",
31282
+ cy: "-15px",
31283
+ rx: targetRadius,
31284
+ ry: targetRadius,
31285
+ fill: "none"
31286
+ }));
31287
+ }
31288
+ const protractorRadius = vec.mag(centerToRotationHandle);
31289
+ function constrainToCircle(edgePoint) {
31290
+ return vec.withMag(edgePoint, protractorRadius);
31291
+ }
31292
+
31293
+ /* The implementation of useDraggablePx is derived from
31294
+ * https://github.com/stevenpetryk/mafs/blob/4520319379a2cc2df8148d8baaef1f85db117103/src/interaction/useMovable.tsx#L20-L83
31295
+ * and copied here under the terms of the MIT license.
31296
+ *
31297
+ * Copyright 2021 Steven Petryk
31298
+ *
31299
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
31300
+ * of this software and associated documentation files (the "Software"), to deal
31301
+ * in the Software without restriction, including without limitation the rights
31302
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31303
+ * copies of the Software, and to permit persons to whom the Software is
31304
+ * furnished to do so, subject to the following conditions:
31305
+ *
31306
+ * The above copyright notice and this permission notice shall be included in
31307
+ * all copies or substantial portions of the Software.
31308
+ */
31309
+ function useDraggablePx(args) {
31310
+ const {
31311
+ gestureTarget: target,
31312
+ onMove,
31313
+ point,
31314
+ constrain = p => p
31315
+ } = args;
31316
+ const pickupPx = React.useRef([0, 0]);
31317
+ useDrag(state => {
31318
+ const {
31319
+ event,
31320
+ first,
31321
+ movement: pixelMovement
31322
+ } = state;
31323
+ event == null || event.stopPropagation();
31324
+ if (first) {
31325
+ pickupPx.current = point;
31326
+ }
31327
+ if (vec.mag(pixelMovement) === 0) {
31328
+ return;
31329
+ }
31330
+ onMove == null || onMove(constrain(vec.add(pickupPx.current, pixelMovement)));
31331
+ }, {
31332
+ target,
31333
+ eventOptions: {
31334
+ passive: false
31335
+ }
31336
+ });
31337
+ }
31338
+
31339
+ // [L]inear Int[erp]olation: gets the weighted average of two values `a` and
31340
+ // `b`, with the given `fraction` specifying how much weight `a` and `b` get:
31341
+ // - if `fraction` is 0, `lerp` returns `a`.
31342
+ // - if `fraction` is 0.5, `lerp` returns the average of `a` and `b`.
31343
+ // - if `fraction` is 1, `lerp` returns `b`.
31344
+ function lerp(a, b, fraction) {
31345
+ return (b - a) * fraction + a;
31346
+ }
31347
+ function degreesToRadians(degrees) {
31348
+ return degrees / 180 * Math.PI;
31349
+ }
31350
+
30998
31351
  function initializeGraphState(params) {
30999
31352
  var _graph$snapTo;
31000
31353
  const {
@@ -31677,19 +32030,6 @@ function boundAndSnapToSides(destinationPoint, {
31677
32030
  return vector$1.add(coordsCopy[rel(-1)], offset);
31678
32031
  }
31679
32032
 
31680
- // Returns the closest point to the given `point` that is within the graph
31681
- // bounds given in `state`.
31682
- function bound$1({
31683
- snapStep,
31684
- range,
31685
- point
31686
- }) {
31687
- const [requestedX, requestedY] = point;
31688
- const [snapX, snapY] = snapStep;
31689
- const [[minX, maxX], [minY, maxY]] = range;
31690
- return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
31691
- }
31692
-
31693
32033
  // Returns the vector from the given point to the top-right corner of the graph when snapped to the grid
31694
32034
  function maxMove({
31695
32035
  snapStep,
@@ -31717,15 +32057,6 @@ function minMove({
31717
32057
  });
31718
32058
  return vec.sub(bottomLeft, point);
31719
32059
  }
31720
- function clamp(value, min, max) {
31721
- if (value < min) {
31722
- return min;
31723
- }
31724
- if (value > max) {
31725
- return max;
31726
- }
31727
- return value;
31728
- }
31729
32060
  const coordsOverlap = coords => coords.some((coord, i) => coords.some((c, j) => i !== j && vector$1.equal(coord, c)));
31730
32061
  function updateAtIndex(args) {
31731
32062
  const {
@@ -31936,7 +32267,7 @@ const MafsGraph = props => {
31936
32267
  }), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
31937
32268
  lockedFigures: props.lockedFigures,
31938
32269
  range: state.range
31939
- }), renderGraph({
32270
+ }), "/* Protractor */", props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31940
32271
  state,
31941
32272
  dispatch
31942
32273
  })))));
@@ -33244,14 +33575,9 @@ class InteractiveGraph extends React.Component {
33244
33575
  }
33245
33576
  render() {
33246
33577
  var _this$props$apiOption;
33247
- if (this.props.graph.type === "polygon" && this.props.graph.numSides === "unlimited") {
33248
- return /*#__PURE__*/React.createElement(LegacyInteractiveGraph, _extends({
33249
- ref: this.legacyGraphRef
33250
- }, this.props));
33251
- }
33252
-
33253
33578
  // Mafs shim
33254
- if ((_this$props$apiOption = this.props.apiOptions) != null && (_this$props$apiOption = _this$props$apiOption.flags) != null && (_this$props$apiOption = _this$props$apiOption["mafs"]) != null && _this$props$apiOption[this.props.graph.type]) {
33579
+ const mafsFlags = (_this$props$apiOption = this.props.apiOptions) == null || (_this$props$apiOption = _this$props$apiOption.flags) == null ? void 0 : _this$props$apiOption["mafs"];
33580
+ if (shouldUseMafs(mafsFlags, this.props.graph)) {
33255
33581
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
33256
33582
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
33257
33583
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
@@ -33812,6 +34138,8 @@ class InteractiveGraph extends React.Component {
33812
34138
  return props.graph;
33813
34139
  }
33814
34140
  }
34141
+
34142
+ // exported for testing
33815
34143
  InteractiveGraph.defaultProps = {
33816
34144
  labels: ["x", "y"],
33817
34145
  range: [[-10, 10], [-10, 10]],
@@ -33827,6 +34155,31 @@ InteractiveGraph.defaultProps = {
33827
34155
  type: "linear"
33828
34156
  }
33829
34157
  };
34158
+ function shouldUseMafs(mafsFlags, graph) {
34159
+ if (typeof mafsFlags === "boolean" || typeof mafsFlags === "undefined") {
34160
+ return false;
34161
+ }
34162
+ switch (graph.type) {
34163
+ case "point":
34164
+ if (graph.numPoints === UNLIMITED) {
34165
+ // TODO(benchristel): add a feature flag for the "unlimited"
34166
+ // case once we've implemented point graphs with unlimited
34167
+ // points
34168
+ return false;
34169
+ }
34170
+ return Boolean(mafsFlags["point"]);
34171
+ case "polygon":
34172
+ if (graph.numSides === UNLIMITED) {
34173
+ // TODO(benchristel): add a feature flag for the "unlimited"
34174
+ // case once we've implemented polygon graphs with unlimited
34175
+ // sides
34176
+ return false;
34177
+ }
34178
+ return Boolean(mafsFlags["polygon"]);
34179
+ default:
34180
+ return Boolean(mafsFlags[graph.type]);
34181
+ }
34182
+ }
33830
34183
  var InteractiveGraph$1 = {
33831
34184
  name: "interactive-graph",
33832
34185
  displayName: "Interactive graph",
@@ -44042,7 +44395,7 @@ const init = function init(options) {
44042
44395
 
44043
44396
  // This file is processed by a Rollup plugin (replace) to inject the production
44044
44397
  const libName = "@khanacademy/perseus";
44045
- const libVersion = "24.0.0";
44398
+ const libVersion = "24.1.0";
44046
44399
  addLibraryVersionToPerseusDebug(libName, libVersion);
44047
44400
 
44048
44401
  const apiVersion = {