@khanacademy/perseus 57.1.0 → 57.2.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.js CHANGED
@@ -24998,7 +24998,7 @@ class GradedGroupAnswerBar extends React.Component {
24998
24998
  }
24999
24999
  }
25000
25000
  GradedGroupAnswerBar.contextType = PerseusI18nContext;
25001
- const fontSize = 17;
25001
+ const fontSize$1 = 17;
25002
25002
  const styles$j = {
25003
25003
  answerBar: {
25004
25004
  display: "flex",
@@ -25023,7 +25023,7 @@ const styles$j = {
25023
25023
  flexDirection: "row",
25024
25024
  alignItems: "center",
25025
25025
  fontWeight: "bold",
25026
- fontSize: fontSize
25026
+ fontSize: fontSize$1
25027
25027
  }
25028
25028
  };
25029
25029
 
@@ -27679,6 +27679,7 @@ const defaultGraphConfig = {
27679
27679
  width: 0,
27680
27680
  height: 0,
27681
27681
  labels: [],
27682
+ labelLocation: "onAxis",
27682
27683
  disableKeyboardInteraction: false,
27683
27684
  interactiveColor: "var(--mafs-blue)"
27684
27685
  };
@@ -27894,6 +27895,8 @@ function escapeIfUnescaped(character) {
27894
27895
  }
27895
27896
  }
27896
27897
 
27898
+ // Exported for testing purposes
27899
+ const fontSize = 14;
27897
27900
  function AxisLabels({
27898
27901
  i18n
27899
27902
  }) {
@@ -27901,24 +27904,27 @@ function AxisLabels({
27901
27904
  range,
27902
27905
  labels,
27903
27906
  width,
27904
- height
27907
+ height,
27908
+ labelLocation
27905
27909
  } = useGraphConfig();
27906
27910
  const {
27907
27911
  strings
27908
27912
  } = i18n;
27909
- const yAxisLabelLocation = [0, range[Y][MAX]];
27910
- const xAxisLabelLocation = [range[X][MAX], 0];
27911
- const [xAxisLabelText, yAxisLabelText] = labels;
27912
27913
  const graphInfo = {
27913
27914
  range,
27914
27915
  width,
27915
27916
  height
27916
27917
  };
27917
- const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
27918
- // The default location for the y-axis-label is at the maximum y point
27919
- // This is just underneath the tick line for that point. The -24 moves the
27920
- // label up one grid square, so it sits on top of the graph
27921
- const [x2, y2] = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
27918
+
27919
+ // Get the position of the main axis labels
27920
+ const [xAxisLabelLocation, yAxisLabelLocation] = getLabelPosition(graphInfo, labelLocation);
27921
+ const [xAxisLabelText, yAxisLabelText] = labels;
27922
+
27923
+ // Get the transform for the labels
27924
+ const {
27925
+ xLabelTransform,
27926
+ yLabelTransform
27927
+ } = getLabelTransform(labelLocation);
27922
27928
  const {
27923
27929
  TeX
27924
27930
  } = getDependencies();
@@ -27926,23 +27932,72 @@ function AxisLabels({
27926
27932
  "aria-label": strings.xAxis,
27927
27933
  style: {
27928
27934
  position: "absolute",
27929
- left: x1,
27930
- top: y1,
27931
- fontSize: "14px",
27932
- transform: "translate(7px, -50%)"
27935
+ left: xAxisLabelLocation[X],
27936
+ top: xAxisLabelLocation[Y],
27937
+ fontSize: fontSize + "px",
27938
+ transform: xLabelTransform
27933
27939
  }
27934
27940
  }, /*#__PURE__*/React__default.createElement(TeX, null, replaceOutsideTeX(xAxisLabelText))), /*#__PURE__*/React__default.createElement("span", {
27935
27941
  "aria-label": strings.yAxis,
27936
27942
  style: {
27937
27943
  position: "absolute",
27938
- left: x2,
27939
- top: y2,
27940
- fontSize: "14px",
27941
- transform: "translate(-50%, 0px)"
27944
+ left: yAxisLabelLocation[X],
27945
+ top: yAxisLabelLocation[Y],
27946
+ fontSize: fontSize + "px",
27947
+ transform: yLabelTransform
27942
27948
  }
27943
27949
  }, /*#__PURE__*/React__default.createElement(TeX, null, replaceOutsideTeX(yAxisLabelText))));
27944
27950
  }
27945
27951
 
27952
+ /* Get the transform for the labels based on the labelLocation
27953
+ * Exported for testing purposes.
27954
+ */
27955
+ const getLabelTransform = labelLocation => {
27956
+ // onAxis is the default label location
27957
+ const isOnAxis = labelLocation === undefined || labelLocation === "onAxis";
27958
+ const xLabelTransform = isOnAxis ? "translate(7px, -50%)" : "translate(-50%, -50%)";
27959
+ const yLabelTransform = isOnAxis ? "translate(-50%, 0px)" : "translate(-50%, 0px) rotate(-90deg)";
27960
+ return {
27961
+ xLabelTransform,
27962
+ yLabelTransform
27963
+ };
27964
+ };
27965
+
27966
+ /* Calculate the position of the main axis labels based on the labelLocation
27967
+ * and the ranges of the graph. Exported for testing purposes.
27968
+ */
27969
+ const getLabelPosition = (graphInfo, labelLocation) => {
27970
+ // If the labels are placed along the edge of the graph, we need to place them at the
27971
+ // center of the graph, which is the average of the min and max values of the axes.
27972
+ if (labelLocation === "alongEdge") {
27973
+ // Offset the labels by a certain amount based on the range of the graph, to ensure that
27974
+ // the labels do not overlap with the axis tick if they are out of the graph bounds.
27975
+ const xAxisLabelOffset = graphInfo.range[Y][MIN] >= 0 ? [0, fontSize * 3] // Move the label down by 3 font sizes if the y-axis min is positive
27976
+ : [0, fontSize]; // Move the label down by 1 font size if the y-axis min is negative
27977
+ const yAxisLabelOffset = graphInfo.range[X][MIN] >= 0 ? [-fontSize * 3, -fontSize] // Move the label left by 3 font sizes if the x-axis min is positive
27978
+ : [-fontSize, -fontSize]; // Move the label left by 1 font size if the x-axis min is negative
27979
+
27980
+ // Calculate the location of the labels to be halfway between the min and max values of the axes
27981
+ const xAxisLabelLocation = [(graphInfo.range[X][MIN] + graphInfo.range[X][MAX]) / 2, graphInfo.range[Y][MIN]];
27982
+ const yAxisLabelLocation = [graphInfo.range[X][MIN], (graphInfo.range[Y][MIN] + graphInfo.range[Y][MAX]) / 2];
27983
+
27984
+ // Convert the Vector2 coordinates to pixel coordinates and add the offsets
27985
+ const _xLabel = vec.add(pointToPixel(xAxisLabelLocation, graphInfo), xAxisLabelOffset);
27986
+ const _yLabel = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), yAxisLabelOffset);
27987
+ return [_xLabel, _yLabel];
27988
+ }
27989
+
27990
+ // Otherwise, the labels are placed on the axes (default), and we need to
27991
+ // place them at the end of the axis, which is the maximum value of the axis.
27992
+ const xLabelInitial = [graphInfo.range[X][MAX], 0];
27993
+ const yLabelInitial = [0, graphInfo.range[Y][MAX]];
27994
+ const yLabelOffset = [0, -fontSize * 2]; // Move the y-axis label up by 2 font sizes
27995
+
27996
+ const xLabel = pointToPixel(xLabelInitial, graphInfo);
27997
+ const yLabel = vec.add(pointToPixel(yLabelInitial, graphInfo), yLabelOffset);
27998
+ return [xLabel, yLabel];
27999
+ };
28000
+
27946
28001
  // The size of the ticks and labels in pixels
27947
28002
  const tickSize = 10;
27948
28003
  const tickLabelSize = 14;
@@ -28873,7 +28928,28 @@ const LockedEllipse = props => {
28873
28928
  }));
28874
28929
  };
28875
28930
 
28931
+ function clampDomain(domain, graphBounds) {
28932
+ // If the domain is invalid, return the graph bounds
28933
+ if (domain[0] > domain[1]) {
28934
+ return graphBounds;
28935
+ }
28936
+
28937
+ // If the domain is outside the graph bounds, return null
28938
+ if (domain[0] < graphBounds[0] && domain[1] < graphBounds[0] || domain[0] > graphBounds[1] && domain[1] > graphBounds[1]) {
28939
+ return null;
28940
+ }
28941
+
28942
+ // Clamp the function to the bounds of the graph to prevent memory
28943
+ // leaks when the domain is set to something like [-Infinity, Infinity].
28944
+ const min = Math.max(domain[0], graphBounds[0]);
28945
+ const max = Math.min(domain[1], graphBounds[1]);
28946
+ return [min, max];
28947
+ }
28948
+
28876
28949
  const LockedFunction = props => {
28950
+ const {
28951
+ range
28952
+ } = useGraphConfig();
28877
28953
  const [equation, setEquation] = useState();
28878
28954
  const {
28879
28955
  color,
@@ -28883,8 +28959,7 @@ const LockedFunction = props => {
28883
28959
  } = props;
28884
28960
  const plotProps = {
28885
28961
  color: lockedFigureColors[color],
28886
- style: strokeStyle,
28887
- domain
28962
+ style: strokeStyle
28888
28963
  };
28889
28964
  const hasAria = !!props.ariaLabel;
28890
28965
  useEffect(() => {
@@ -28895,6 +28970,12 @@ const LockedFunction = props => {
28895
28970
  if (typeof equation === "undefined") {
28896
28971
  return null;
28897
28972
  }
28973
+ const clampedDomain = directionalAxis === "x" ? clampDomain(domain, range[0]) : clampDomain(domain, range[1]);
28974
+
28975
+ // The domain entirely is outside the bounds of the graph. Don't render.
28976
+ if (clampedDomain === null) {
28977
+ return null;
28978
+ }
28898
28979
  return /*#__PURE__*/React.createElement("g", {
28899
28980
  className: "locked-function",
28900
28981
  "aria-label": hasAria ? props.ariaLabel : undefined,
@@ -28903,11 +28984,13 @@ const LockedFunction = props => {
28903
28984
  }, directionalAxis === "x" && /*#__PURE__*/React.createElement(Plot$2.OfX, _extends({
28904
28985
  y: x => equation.eval({
28905
28986
  x
28906
- })
28987
+ }),
28988
+ domain: clampedDomain
28907
28989
  }, plotProps)), directionalAxis === "y" && /*#__PURE__*/React.createElement(Plot$2.OfY, _extends({
28908
28990
  x: y => equation.eval({
28909
28991
  y
28910
- })
28992
+ }),
28993
+ domain: clampedDomain
28911
28994
  }, plotProps)));
28912
28995
  };
28913
28996
 
@@ -34200,6 +34283,7 @@ const MafsGraph = props => {
34200
34283
  state,
34201
34284
  dispatch,
34202
34285
  labels,
34286
+ labelLocation,
34203
34287
  readOnly,
34204
34288
  fullGraphAriaLabel,
34205
34289
  fullGraphAriaDescription
@@ -34280,6 +34364,7 @@ const MafsGraph = props => {
34280
34364
  width,
34281
34365
  height,
34282
34366
  labels,
34367
+ labelLocation,
34283
34368
  disableKeyboardInteraction: disableInteraction,
34284
34369
  // If the graph is read-only or static, we want to make it
34285
34370
  // visually clear that the graph is no longer interactive.
@@ -41391,7 +41476,7 @@ const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
41391
41476
 
41392
41477
  // This file is processed by a Rollup plugin (replace) to inject the production
41393
41478
  const libName = "@khanacademy/perseus";
41394
- const libVersion = "57.1.0";
41479
+ const libVersion = "57.2.1";
41395
41480
  addLibraryVersionToPerseusDebug(libName, libVersion);
41396
41481
 
41397
41482
  const apiVersion = {