@khanacademy/perseus 22.0.1 → 22.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.
@@ -1,14 +1,6 @@
1
1
  import * as React from "react";
2
2
  type Props = {
3
- children: React.ReactNode;
3
+ children: string | React.ReactElement | React.ReactElement[];
4
4
  };
5
- type State = {
6
- hover: boolean;
7
- };
8
- declare class InfoTip extends React.Component<Props, State> {
9
- state: State;
10
- handleMouseEnter: () => void;
11
- handleMouseLeave: () => void;
12
- render(): React.ReactNode;
13
- }
5
+ export declare const InfoTip: (props: Props) => JSX.Element;
14
6
  export default InfoTip;
@@ -83,6 +83,7 @@ declare class MathInput extends React.Component<Props, State> {
83
83
  }>;
84
84
  context: React.ContextType<typeof MathInputI18nContext>;
85
85
  inputRef: React.RefObject<InnerMathInput>;
86
+ blur(): void;
86
87
  focus(): void;
87
88
  insert(value: any): void;
88
89
  render(): JSX.Element;
package/dist/es/index.css CHANGED
@@ -2470,7 +2470,10 @@ img.zoom-img {
2470
2470
  padding-top: 5px;
2471
2471
  }
2472
2472
  .framework-perseus .svg-image {
2473
- display: inline-block;
2473
+ display: block;
2474
+ margin: 0 auto;
2475
+ margin-inline-start: auto;
2476
+ margin-inline-end: auto;
2474
2477
  }
2475
2478
  .framework-perseus .unresponsive-svg-image,
2476
2479
  .framework-perseus .perseus-rendered-radio .unresponsive-svg-image {
@@ -2750,6 +2753,16 @@ body.mobile .categorizer-container td.category input[type="radio"] + span:active
2750
2753
  .perseus-image-editor .graph-settings {
2751
2754
  margin-top: 5px;
2752
2755
  }
2756
+ .perseus-image-widget {
2757
+ margin-inline-start: auto;
2758
+ margin-inline-end: auto;
2759
+ }
2760
+ .perseus-hint-renderer .perseus-image-widget {
2761
+ margin-inline-start: 0;
2762
+ }
2763
+ .perseus-hint-renderer .svg-image {
2764
+ margin-inline-start: 0;
2765
+ }
2753
2766
  .perseus-widget-interactive-graph {
2754
2767
  box-sizing: content-box;
2755
2768
  padding: 25px 25px 0 0;
package/dist/es/index.js CHANGED
@@ -29,10 +29,10 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
29
29
  import caretDown from '@phosphor-icons/core/assets/regular/caret-down.svg';
30
30
  import caretUp from '@phosphor-icons/core/assets/regular/caret-up.svg';
31
31
  import debounce$2 from 'lodash.debounce';
32
- import { Line as Line$3, Point as Point$2, useTransformContext, vec, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
32
+ import { vec, Line as Line$3, Point as Point$2, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
33
33
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
34
+ import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
34
35
  import questionIcon from '@phosphor-icons/core/regular/question.svg';
35
- import { LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
36
36
  import createReactClass from 'create-react-class';
37
37
  import Switch from '@khanacademy/wonder-blocks-switch';
38
38
  import Pill from '@khanacademy/wonder-blocks-pill';
@@ -542,13 +542,17 @@ class MathInput extends React.Component {
542
542
  super(...args);
543
543
  this.inputRef = /*#__PURE__*/React.createRef();
544
544
  }
545
- focus() {
545
+ blur() {
546
546
  var _this$inputRef$curren;
547
- (_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus();
547
+ (_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.blur();
548
548
  }
549
- insert(value) {
549
+ focus() {
550
550
  var _this$inputRef$curren2;
551
- (_this$inputRef$curren2 = this.inputRef.current) == null || _this$inputRef$curren2.insert(value);
551
+ (_this$inputRef$curren2 = this.inputRef.current) == null || _this$inputRef$curren2.focus();
552
+ }
553
+ insert(value) {
554
+ var _this$inputRef$curren3;
555
+ (_this$inputRef$curren3 = this.inputRef.current) == null || _this$inputRef$curren3.insert(value);
552
556
  }
553
557
  render() {
554
558
  return /*#__PURE__*/React.createElement(InnerMathInput, _extends({}, this.props, {
@@ -2323,7 +2327,12 @@ class Expression extends React.Component {
2323
2327
  var _this$refs$input;
2324
2328
  // eslint-disable-next-line react/no-string-refs
2325
2329
  // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
2326
- (_this$refs$input = this.refs.input) == null || _this$refs$input.blur();
2330
+ if (typeof ((_this$refs$input = this.refs.input) == null ? void 0 : _this$refs$input.blur) === "function") {
2331
+ var _this$refs$input2;
2332
+ // eslint-disable-next-line react/no-string-refs
2333
+ // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
2334
+ (_this$refs$input2 = this.refs.input) == null || _this$refs$input2.blur();
2335
+ }
2327
2336
  };
2328
2337
  this.getInputPaths = () => {
2329
2338
  // The widget itself is an input, so we return a single empty list to
@@ -21720,10 +21729,15 @@ class SimpleKeypadInput extends React.Component {
21720
21729
 
21721
21730
  blur() {
21722
21731
  var _this$refs$input;
21732
+ // eslint-disable-next-line react/no-string-refs
21723
21733
  // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
21724
- (_this$refs$input = this.refs.input) == null || _this$refs$input.blur(); // eslint-disable-line react/no-string-refs
21734
+ if (typeof ((_this$refs$input = this.refs.input) == null ? void 0 : _this$refs$input.blur) === "function") {
21735
+ var _this$refs$input2;
21736
+ // eslint-disable-next-line react/no-string-refs
21737
+ // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
21738
+ (_this$refs$input2 = this.refs.input) == null || _this$refs$input2.blur();
21739
+ }
21725
21740
  }
21726
-
21727
21741
  getValue() {
21728
21742
  return this.props.value;
21729
21743
  }
@@ -21871,7 +21885,12 @@ class InputNumber extends React.Component {
21871
21885
  var _this$refs$input;
21872
21886
  // eslint-disable-next-line react/no-string-refs
21873
21887
  // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
21874
- (_this$refs$input = this.refs.input) == null || _this$refs$input.blur();
21888
+ if (typeof ((_this$refs$input = this.refs.input) == null ? void 0 : _this$refs$input.blur) === "function") {
21889
+ var _this$refs$input2;
21890
+ // eslint-disable-next-line react/no-string-refs
21891
+ // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
21892
+ (_this$refs$input2 = this.refs.input) == null || _this$refs$input2.blur();
21893
+ }
21875
21894
  };
21876
21895
  this.getInputPaths = () => {
21877
21896
  // The widget itself is an input, so we return a single empty list to
@@ -28458,6 +28477,89 @@ Graph.defaultProps = {
28458
28477
  isMobile: false
28459
28478
  };
28460
28479
 
28480
+ const defaultGraphConfig = {
28481
+ range: [[0, 1], [0, 1]],
28482
+ snapStep: [1, 1],
28483
+ markings: "none",
28484
+ showTooltips: false,
28485
+ graphDimensionsInPixels: [1, 1],
28486
+ width: 0,
28487
+ height: 0,
28488
+ labels: []
28489
+ };
28490
+ const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
28491
+ function useGraphConfig() {
28492
+ return React__default.useContext(GraphConfigContext);
28493
+ }
28494
+
28495
+ const matrixBuilder = vec.matrixBuilder;
28496
+ // When converting vectors, we don't need to translate because a vector
28497
+ // is like a pair of x and y distances, which are not bound to specific graph locations.
28498
+ function vectorsToPixels(vectors, graphState, translation = [0, 0]) {
28499
+ const {
28500
+ range,
28501
+ width,
28502
+ height
28503
+ } = graphState;
28504
+ const [[xMin, xMax], [yMin, yMax]] = range;
28505
+ const transformToPx = matrixBuilder().translate(...translation).scale(width / (xMax - xMin), -height / (yMax - yMin)).get();
28506
+ return vectors.map(p => vec.transform(p, transformToPx));
28507
+ }
28508
+
28509
+ // When converting points, we translate so the pixel point ends up in the right location.
28510
+ // This is because points are specific locations on graphs, unlike vectors.
28511
+ function pointToPixel(point, graphState) {
28512
+ const [[xMin], [, yMax]] = graphState.range;
28513
+ const [a, b] = [-xMin, -yMax];
28514
+ return vectorsToPixels([point], graphState, [a, b])[0];
28515
+ }
28516
+ const useTransformVectorsToPixels = (...vectors) => {
28517
+ const graphState = useGraphConfig();
28518
+ return vectorsToPixels(vectors, graphState);
28519
+ };
28520
+
28521
+ function AxisLabels() {
28522
+ const {
28523
+ range,
28524
+ labels,
28525
+ width,
28526
+ height
28527
+ } = useGraphConfig();
28528
+ const yAxisLabelLocation = [0, range[1][1]];
28529
+ const xAxisLabelLocation = [range[0][1], 0];
28530
+ const [xAxisLabelText, yAxisLabelText] = labels;
28531
+ const graphInfo = {
28532
+ range,
28533
+ width,
28534
+ height
28535
+ };
28536
+ const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
28537
+ // The default location for the y-axis-label is at the maximum y point
28538
+ // This is just underneath the tick line for that point. The -24 moves the
28539
+ // label up one grid square, so it sits on top of the graph
28540
+ const [x2, y2] = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
28541
+ const {
28542
+ TeX
28543
+ } = getDependencies();
28544
+ return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
28545
+ style: {
28546
+ position: "absolute",
28547
+ left: x1,
28548
+ top: y1,
28549
+ fontSize: "14px",
28550
+ transform: "translate(7px, -50%)"
28551
+ }
28552
+ }, /*#__PURE__*/React__default.createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default.createElement("span", {
28553
+ style: {
28554
+ position: "absolute",
28555
+ left: x2,
28556
+ top: y2,
28557
+ fontSize: "14px",
28558
+ transform: "translate(-50%, 0px)"
28559
+ }
28560
+ }, /*#__PURE__*/React__default.createElement(TeX, null, yAxisLabelText)));
28561
+ }
28562
+
28461
28563
  // TODO(FEI-4010): Remove `Perseus` prefix for all types here
28462
28564
  // TODO(FEI-4011): Use types generated by https://github.com/jaredly/generate-perseus-flowtypes
28463
28565
 
@@ -28749,26 +28851,17 @@ function snap(snapStep, point) {
28749
28851
  return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
28750
28852
  }
28751
28853
 
28752
- const useTransform = (...points) => {
28753
- const {
28754
- viewTransform,
28755
- userTransform
28756
- } = useTransformContext();
28757
- const transformToPx = vec.matrixMult(viewTransform, userTransform);
28758
- const transformedPoints = points.map(point => vec.transform(point, transformToPx));
28759
- return transformedPoints;
28760
- };
28761
-
28762
28854
  /**
28763
- * Given two points, find the tips that extends through the points to the edge of the range.
28764
- * @param collinearPoint - The point that the line passes through. Needed to establish slope.
28765
- * @param extendFrom - The point that the line extends from to the edge of the graph.
28855
+ * Given a ray and a rectangular box, find the point where the ray intersects
28856
+ * the edge of the box. Assumes the `initialPoint` is inside the box.
28857
+ * @param initialPoint - The starting point of the ray.
28858
+ * @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
28859
+ * @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
28766
28860
  */
28767
- const getRayIntersectionCoords = (collinearPoint, extendFrom, range) => {
28768
- // edges of the graph
28769
- const [[xMin, xMax], [yMin, yMax]] = range;
28770
- const [aX, aY] = collinearPoint;
28771
- const [bX, bY] = extendFrom;
28861
+ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
28862
+ const [[xMin, xMax], [yMin, yMax]] = box;
28863
+ const [aX, aY] = throughPoint;
28864
+ const [bX, bY] = initialPoint;
28772
28865
  const yDiff = bY - aY;
28773
28866
  const xDiff = bX - aX;
28774
28867
  const slope = yDiff / xDiff;
@@ -28872,7 +28965,7 @@ function scaleCommandBy(scaleFactor) {
28872
28965
  const arrowPath = pathBuilder().move(-3, 4).curve(-2.75, 2.5, 0, 0.25, 0.75, 0).curve(0, -0.25, -2.75, -2.5, -3, -4).scale(1.4).build();
28873
28966
  function Arrowhead(props) {
28874
28967
  var _props$color;
28875
- const [point] = useTransform([props.x, props.y]);
28968
+ const [point] = useTransformVectorsToPixels(props.tip);
28876
28969
  return /*#__PURE__*/React.createElement("g", {
28877
28970
  transform: `translate(${point[0]} ${point[1]}) rotate(${-props.angle})`
28878
28971
  }, /*#__PURE__*/React.createElement("g", {
@@ -28894,7 +28987,7 @@ function Vector(props) {
28894
28987
  tip,
28895
28988
  color
28896
28989
  } = props;
28897
- const [tailPx, tipPx] = useTransform(tail, tip);
28990
+ const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
28898
28991
  const direction = vec.sub(tip, tail);
28899
28992
  return /*#__PURE__*/React.createElement("g", {
28900
28993
  style: {
@@ -28905,8 +28998,7 @@ function Vector(props) {
28905
28998
  start: tailPx,
28906
28999
  end: tipPx
28907
29000
  }), /*#__PURE__*/React.createElement(Arrowhead, {
28908
- x: tip[0],
28909
- y: tip[1],
29001
+ tip: tip,
28910
29002
  angle: angleDegrees(direction)
28911
29003
  }));
28912
29004
  }
@@ -28924,12 +29016,16 @@ const MovableLine = props => {
28924
29016
  stroke = defaultStroke
28925
29017
  } = props;
28926
29018
  const midpoint = vec.midpoint(start, end);
28927
- const [startPtPx, endPtPx] = useTransform(start, end);
29019
+ const [startPtPx, endPtPx] = useTransformVectorsToPixels(start, end);
29020
+ const {
29021
+ graphDimensionsInPixels
29022
+ } = useGraphConfig();
28928
29023
  let startExtend = undefined;
28929
29024
  let endExtend = undefined;
28930
29025
  if (extend) {
28931
- startExtend = extend.start ? getRayIntersectionCoords(end, start, extend.range) : undefined;
28932
- endExtend = extend.end ? getRayIntersectionCoords(start, end, extend.range) : undefined;
29026
+ const trimmedRange = trimRange(extend.range, graphDimensionsInPixels);
29027
+ startExtend = extend.start ? getIntersectionOfRayWithBox(start, end, trimmedRange) : undefined;
29028
+ endExtend = extend.end ? getIntersectionOfRayWithBox(end, start, trimmedRange) : undefined;
28933
29029
  }
28934
29030
  const line = useRef(null);
28935
29031
  const {
@@ -28986,16 +29082,24 @@ const MovableLine = props => {
28986
29082
  color: stroke
28987
29083
  }));
28988
29084
  };
28989
-
28990
- const defaultGraphConfig = {
28991
- range: [[0, 1], [0, 1]],
28992
- snapStep: [1, 1],
28993
- markings: "none",
28994
- showTooltips: false
28995
- };
28996
- const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
28997
- function useGraphConfig() {
28998
- return React__default.useContext(GraphConfigContext);
29085
+ function trimRange(range, graphDimensionsInPixels) {
29086
+ const pixelsToTrim = 4;
29087
+ const [xRange, yRange] = range;
29088
+ const [pixelsWide, pixelsTall] = graphDimensionsInPixels;
29089
+ const graphUnitsPerPixelX = size(xRange) / pixelsWide;
29090
+ const graphUnitsPerPixelY = size(yRange) / pixelsTall;
29091
+ const graphUnitsToTrimX = pixelsToTrim * graphUnitsPerPixelX;
29092
+ const graphUnitsToTrimY = pixelsToTrim * graphUnitsPerPixelY;
29093
+ return [trim(xRange, graphUnitsToTrimX), trim(yRange, graphUnitsToTrimY)];
29094
+ }
29095
+ function trim(interval, amount) {
29096
+ if (size(interval) < amount * 2) {
29097
+ return [0, 0];
29098
+ }
29099
+ return [interval[0] + amount, interval[1] - amount];
29100
+ }
29101
+ function size(interval) {
29102
+ return interval[1] - interval[0];
28999
29103
  }
29000
29104
 
29001
29105
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
@@ -29028,13 +29132,13 @@ const StyledMovablePoint = props => {
29028
29132
  constrain: p => snap(snapStep, p)
29029
29133
  });
29030
29134
  const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""}`;
29031
- const [[x, y]] = useTransform(point);
29135
+ const [[x, y]] = useTransformVectorsToPixels(point);
29032
29136
  const [xMin, xMax] = range[0];
29033
29137
  const [yMin, yMax] = range[1];
29034
- const [[verticalStartX]] = useTransform([xMin, 0]);
29035
- const [[verticalEndX]] = useTransform([xMax, 0]);
29036
- const [[_, horizontalStartY]] = useTransform([0, yMin]);
29037
- const [[__, horizontalEndY]] = useTransform([0, yMax]);
29138
+ const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
29139
+ const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
29140
+ const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
29141
+ const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
29038
29142
  const showHairlines = dragging && markings !== "none";
29039
29143
  const hairlines = /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("line", {
29040
29144
  x1: verticalStartX,
@@ -29356,7 +29460,7 @@ const isRightAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
29356
29460
  */
29357
29461
  const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
29358
29462
  // Create a ray from the midpoint (inside angle) to the edge of the range
29359
- const rangeIntersectionPoint = getRayIntersectionCoords(midpoint, vertex, range);
29463
+ const rangeIntersectionPoint = getIntersectionOfRayWithBox(vertex, midpoint, range);
29360
29464
  let lineIntersectionCount = 0;
29361
29465
  polygonLines.forEach(line => linesIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
29362
29466
 
@@ -29479,23 +29583,19 @@ function AxisArrows() {
29479
29583
  const axisColor = "var(--mafs-fg)";
29480
29584
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrowhead, {
29481
29585
  color: axisColor,
29482
- x: xMin,
29483
- y: 0,
29586
+ tip: [xMin, 0],
29484
29587
  angle: 180
29485
29588
  }), /*#__PURE__*/React__default.createElement(Arrowhead, {
29486
29589
  color: axisColor,
29487
- x: xMax,
29488
- y: 0,
29590
+ tip: [xMax, 0],
29489
29591
  angle: 0
29490
29592
  }), /*#__PURE__*/React__default.createElement(Arrowhead, {
29491
29593
  color: axisColor,
29492
- x: 0,
29493
- y: yMin,
29594
+ tip: [0, yMin],
29494
29595
  angle: 270
29495
29596
  }), /*#__PURE__*/React__default.createElement(Arrowhead, {
29496
29597
  color: axisColor,
29497
- x: 0,
29498
- y: yMax,
29598
+ tip: [0, yMax],
29499
29599
  angle: 90
29500
29600
  }));
29501
29601
  }
@@ -29520,7 +29620,7 @@ const YGridTick = ({
29520
29620
  tickStep
29521
29621
  }) => {
29522
29622
  const pointOnAxis = [0, y];
29523
- const [[xPosition, yPosition]] = useTransform(pointOnAxis);
29623
+ const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
29524
29624
  return /*#__PURE__*/React.createElement("g", {
29525
29625
  className: "y-axis-ticks"
29526
29626
  }, /*#__PURE__*/React.createElement("line", {
@@ -29543,7 +29643,7 @@ const XGridTick = ({
29543
29643
  tickStep
29544
29644
  }) => {
29545
29645
  const pointOnAxis = [x, 0];
29546
- const [[xPosition, yPosition]] = useTransform(pointOnAxis);
29646
+ const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
29547
29647
  return /*#__PURE__*/React.createElement("g", {
29548
29648
  className: "x-axis-ticks"
29549
29649
  }, /*#__PURE__*/React.createElement("line", {
@@ -29764,97 +29864,26 @@ const styles$k = StyleSheet.create({
29764
29864
  }
29765
29865
  });
29766
29866
 
29767
- const colors$1 = {
29768
- grayLight: "#aaa",
29769
- basicBorderColor: "#ccc",
29770
- white: "#fff"
29771
- };
29772
- const triangleBeforeAfter = {
29773
- borderBottom: "9px solid transparent",
29774
- borderTop: "9px solid transparent",
29775
- content: '" "',
29776
- height: "0",
29777
- position: "absolute",
29778
- top: "0",
29779
- width: "0"
29867
+ const InfoTip$1 = props => {
29868
+ return /*#__PURE__*/React.createElement(Tooltip$1, {
29869
+ content: /*#__PURE__*/React.createElement(Body, {
29870
+ style: styles$j.body
29871
+ }, props.children),
29872
+ placement: "right"
29873
+ }, /*#__PURE__*/React.createElement(PhosphorIcon, {
29874
+ size: "small",
29875
+ icon: questionIcon,
29876
+ style: styles$j.spacingLeft
29877
+ }));
29780
29878
  };
29781
29879
  const styles$j = StyleSheet.create({
29782
- infoTip: {
29783
- display: "inline-block",
29784
- marginLeft: "5px",
29785
- position: "relative"
29786
- },
29787
- infoTipContainer: {
29788
- position: "absolute",
29789
- top: "-12px",
29790
- left: "22px",
29791
- zIndex: 1000
29792
- },
29793
- infoTipTriangle: {
29794
- height: "10px",
29795
- left: "0",
29796
- position: "absolute",
29797
- top: "8px",
29798
- width: "0",
29799
- zIndex: 1,
29800
- ":before": _extends({}, triangleBeforeAfter, {
29801
- borderRight: "9px solid #bbb",
29802
- right: "0"
29803
- }),
29804
- ":after": _extends({}, triangleBeforeAfter, {
29805
- borderRight: `9px solid ${colors$1.white}`,
29806
- right: "-1px"
29807
- })
29808
- },
29809
- verticalShadow: {
29810
- border: `1px solid ${colors$1.basicBorderColor}`,
29811
- boxShadow: `0 1px 3px ${colors$1.basicBorderColor}`,
29812
- borderBottom: `1px solid ${colors$1.grayLight}`
29880
+ spacingLeft: {
29881
+ marginInlineStart: spacing.xxxSmall_4
29813
29882
  },
29814
- infoTipContentContainer: {
29815
- background: colors$1.white,
29816
- padding: "5px 10px",
29817
- width: "240px"
29883
+ body: {
29884
+ padding: spacing.medium_16
29818
29885
  }
29819
29886
  });
29820
- class InfoTip$1 extends React.Component {
29821
- constructor(...args) {
29822
- super(...args);
29823
- this.state = {
29824
- hover: false
29825
- };
29826
- this.handleMouseEnter = () => {
29827
- this.setState({
29828
- hover: true
29829
- });
29830
- };
29831
- this.handleMouseLeave = () => {
29832
- this.setState({
29833
- hover: false
29834
- });
29835
- };
29836
- }
29837
- render() {
29838
- return /*#__PURE__*/React.createElement("div", {
29839
- className: css(styles$j.infoTip)
29840
- }, /*#__PURE__*/React.createElement(View, {
29841
- onMouseEnter: this.handleMouseEnter,
29842
- onMouseLeave: this.handleMouseLeave
29843
- }, /*#__PURE__*/React.createElement(PhosphorIcon, {
29844
- size: "small",
29845
- icon: questionIcon
29846
- })), /*#__PURE__*/React.createElement("div", {
29847
- className: css(styles$j.infoTipContainer),
29848
- style: {
29849
- display: this.state.hover ? "block" : "none"
29850
- }
29851
- }, /*#__PURE__*/React.createElement("div", {
29852
- className: css(styles$j.infoTipTriangle)
29853
- }), /*#__PURE__*/React.createElement("div", {
29854
- className: css(styles$j.verticalShadow, styles$j.infoTipContentContainer)
29855
- }, this.props.children)));
29856
- }
29857
- }
29858
29887
 
29859
29888
  /**
29860
29889
  * A wrapper around info-tip/info-tip.jsx that can be rendered on the
@@ -30694,9 +30723,7 @@ const getDefaultPoints = ({
30694
30723
  // Instead of updating to v5, using generic type to relate input and output types.
30695
30724
  function getGradableGraph(state, initialGraph) {
30696
30725
  if (!state.hasBeenInteractedWith) {
30697
- return {
30698
- type: initialGraph.type
30699
- };
30726
+ return _extends({}, initialGraph);
30700
30727
  }
30701
30728
  if (initialGraph.type === "linear-system" && state.type === "linear-system") {
30702
30729
  return _extends({}, initialGraph, {
@@ -30855,7 +30882,8 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30855
30882
  const MafsGraph = props => {
30856
30883
  const {
30857
30884
  state,
30858
- dispatch
30885
+ dispatch,
30886
+ labels
30859
30887
  } = props;
30860
30888
  const [width, height] = props.box;
30861
30889
  const prevState = useRef(state);
@@ -30884,7 +30912,11 @@ const MafsGraph = props => {
30884
30912
  range: state.range,
30885
30913
  snapStep: state.snapStep,
30886
30914
  markings: props.markings,
30887
- showTooltips: !!props.showTooltips
30915
+ showTooltips: !!props.showTooltips,
30916
+ graphDimensionsInPixels: props.box,
30917
+ width,
30918
+ height,
30919
+ labels
30888
30920
  }
30889
30921
  }, /*#__PURE__*/React.createElement(View, {
30890
30922
  style: {
@@ -30903,7 +30935,7 @@ const MafsGraph = props => {
30903
30935
  bottom: 0,
30904
30936
  left: 0
30905
30937
  }
30906
- }, /*#__PURE__*/React.createElement(Mafs, {
30938
+ }, props.markings === "graph" && /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(Mafs, {
30907
30939
  preserveAspectRatio: false,
30908
30940
  viewBox: {
30909
30941
  x: props.range[0],
@@ -32085,8 +32117,6 @@ class LegacyInteractiveGraph extends React.Component {
32085
32117
  graph: graph
32086
32118
  });
32087
32119
  };
32088
- this.getUserInput = () => InteractiveGraph.getUserInputFromProps(this.props);
32089
- this.simpleValidate = rubric => InteractiveGraph.validate(this.getUserInput(), rubric, this);
32090
32120
  this.focus = $.noop;
32091
32121
  }
32092
32122
  // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
@@ -32138,6 +32168,12 @@ class LegacyInteractiveGraph extends React.Component {
32138
32168
  areSegmentGraphsEqual(prevGraph, currentGraph) {
32139
32169
  return prevGraph.numSegments !== currentGraph.numSegments;
32140
32170
  }
32171
+ getUserInput() {
32172
+ return InteractiveGraph.getUserInputFromProps(this.props);
32173
+ }
32174
+ simpleValidate(rubric) {
32175
+ return InteractiveGraph.validate(this.getUserInput(), rubric, this);
32176
+ }
32141
32177
  render() {
32142
32178
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
32143
32179
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
@@ -32219,10 +32255,10 @@ class InteractiveGraph extends React.Component {
32219
32255
  }
32220
32256
  throw new PerseusError("Cannot getUserInput from a graph that has never rendered", Errors.NotAllowed);
32221
32257
  };
32222
- this.simpleValidate = rubric => {
32223
- var _this$getUserInput;
32224
- return InteractiveGraph.validate((_this$getUserInput = this.getUserInput) == null ? void 0 : _this$getUserInput.call(this), rubric, this);
32225
- };
32258
+ }
32259
+ simpleValidate(rubric) {
32260
+ var _this$getUserInput;
32261
+ return InteractiveGraph.validate((_this$getUserInput = this.getUserInput) == null ? void 0 : _this$getUserInput.call(this), rubric, this);
32226
32262
  }
32227
32263
  render() {
32228
32264
  var _this$props$apiOption;
@@ -43018,7 +43054,7 @@ const init = function init(options) {
43018
43054
 
43019
43055
  // This file is processed by a Rollup plugin (replace) to inject the production
43020
43056
  const libName = "@khanacademy/perseus";
43021
- const libVersion = "22.0.1";
43057
+ const libVersion = "22.1.0";
43022
43058
  addLibraryVersionToPerseusDebug(libName, libVersion);
43023
43059
 
43024
43060
  const apiVersion = {