@khanacademy/perseus 32.0.1 → 33.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.js CHANGED
@@ -10035,11 +10035,36 @@ const setLabelMargins = function setLabelMargins(span, size) {
10035
10035
  marginTop: -height / 2 - y * scale
10036
10036
  });
10037
10037
  } else {
10038
+ var _$container$css, _$container$width, _$span$css;
10039
+ const $container = $span.closest(".svg-image");
10040
+ // Ensuring that the line-height of the text doesn't throw off placement of the text element.
10041
+ // Inherited line-height values can really mess up placement.
10042
+ $container.css("line-height", "normal");
10043
+
10044
+ // The 'max-width' value is the same as the expected (100% scale) width of the graphie.
10045
+ // We are using jQuery to get this information since we don't have a way to pass it to this function.
10046
+ const expectedWidth = (_$container$css = $container.css("max-width")) != null ? _$container$css : "0px";
10047
+ // We can calculate the true scale of the graphie by taking actual width and dividing by the expected width.
10048
+ // NOTE: Using 'slice' to remove the "px" unit from the end of the expected width value.
10049
+ const scale = ((_$container$width = $container.width()) != null ? _$container$width : 0) / parseInt(expectedWidth.replace(/px$/, "")) * 100;
10050
+
10051
+ // Any padding needs to be scaled accordingly.
10052
+ const currentPadding = (_$span$css = $span.css("padding")) != null ? _$span$css : "0px";
10053
+ const newPadding = Math.round(parseInt(currentPadding.slice(0, -2)) * scale) / 100;
10054
+
10055
+ // "multipliers" basically move the position of the text by using 1, 0, -1
10056
+ // 'margin-left' and 'margin-top' are used to position the text.
10057
+ // Margin and font size need to be scaled accordingly.
10038
10058
  const multipliers = labelDirections[direction || "center"];
10039
- $span.css({
10040
- marginLeft: Math.round(width * multipliers[0]),
10041
- marginTop: Math.round(height * multipliers[1])
10042
- });
10059
+ const styling = {
10060
+ marginLeft: Math.round(width * multipliers[0] * scale) / 100,
10061
+ marginTop: Math.round(height * multipliers[1] * scale) / 100,
10062
+ padding: `${newPadding}px`
10063
+ };
10064
+ if (scale !== 1) {
10065
+ styling["fontSize"] = `${Math.round(scale * 100) / 100}%`;
10066
+ }
10067
+ $span.css(styling);
10043
10068
  }
10044
10069
  };
10045
10070
  const GraphUtils = {
@@ -17231,9 +17256,16 @@ class SvgImage extends React.Component {
17231
17256
  // without coordinates. They don't seem to have any content, so
17232
17257
  // it seems fine to just ignore them (rather than error), but
17233
17258
  // we should figure out why this is happening.
17234
- const label = graphie.label(labelData.coordinates, labelData.content, labelData.alignment, labelData.typesetAsMath, {
17259
+
17260
+ // 'styling' - When a default scale (1) is used,
17261
+ // setting the font size to 100% is redundant.
17262
+ // Also, setting the font size to 100% can counteract other scale-based styling.
17263
+ // Therefore, when the scale is not applicable,
17264
+ // we set the value to 'null' so that no additional styling will be set.
17265
+ const styling = this.props.scale !== 1 ? {
17235
17266
  "font-size": 100 * this.props.scale + "%"
17236
- });
17267
+ } : null;
17268
+ const label = graphie.label(labelData.coordinates, labelData.content, labelData.alignment, labelData.typesetAsMath, styling);
17237
17269
 
17238
17270
  // Convert absolute positioning css from pixels to percentages
17239
17271
  // TODO(alex): Dynamically resize font-size as well. This
@@ -28288,6 +28320,94 @@ Graph.defaultProps = {
28288
28320
  isMobile: false
28289
28321
  };
28290
28322
 
28323
+ function pathBuilder() {
28324
+ return new PathBuilder();
28325
+ }
28326
+ class PathBuilder {
28327
+ constructor() {
28328
+ this.path = [];
28329
+ this.scaleFactor = 1;
28330
+ }
28331
+ build() {
28332
+ return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
28333
+ }
28334
+
28335
+ // add a move (M) command to the path
28336
+ move(x, y) {
28337
+ this.path.push({
28338
+ action: "M",
28339
+ args: [x, y]
28340
+ });
28341
+ return this;
28342
+ }
28343
+ line(x, y) {
28344
+ this.path.push({
28345
+ action: "L",
28346
+ args: [x, y]
28347
+ });
28348
+ return this;
28349
+ }
28350
+ circularArc(radius, toX, toY, {
28351
+ sweep = false,
28352
+ largeArc = false
28353
+ } = {}) {
28354
+ this.path.push({
28355
+ action: "A",
28356
+ args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
28357
+ });
28358
+ return this;
28359
+ }
28360
+
28361
+ // add a curve (C) command to the path
28362
+ curve(control1X, control1Y, control2X, control2Y, endX, endY) {
28363
+ this.path.push({
28364
+ action: "C",
28365
+ args: [control1X, control1Y, control2X, control2Y, endX, endY]
28366
+ });
28367
+ return this;
28368
+ }
28369
+
28370
+ // Scale all coordinates of the path by a factor
28371
+ scale(factor) {
28372
+ this.scaleFactor *= factor;
28373
+ return this;
28374
+ }
28375
+ }
28376
+ function commandToString(command) {
28377
+ return `${command.action}${command.args.join(" ")}`;
28378
+ }
28379
+ function scaleCommandBy(scaleFactor) {
28380
+ return command => {
28381
+ switch (command.action) {
28382
+ case "A":
28383
+ // TODO(benchristel): do we want to refactor this switch to
28384
+ // use polymorphism?
28385
+ // Arc command
28386
+ return _extends({}, command, {
28387
+ args: [command.args[0] * scaleFactor,
28388
+ // x radius
28389
+ command.args[1] * scaleFactor,
28390
+ // y radius
28391
+ command.args[2],
28392
+ // rotation
28393
+ command.args[3],
28394
+ // largeArc flag
28395
+ command.args[4],
28396
+ // sweep flag
28397
+ command.args[5] * scaleFactor,
28398
+ // end x
28399
+ command.args[6] * scaleFactor // end y
28400
+ ]
28401
+ });
28402
+
28403
+ default:
28404
+ return _extends({}, command, {
28405
+ args: command.args.map(c => c * scaleFactor)
28406
+ });
28407
+ }
28408
+ };
28409
+ }
28410
+
28291
28411
  // Restricts `value` to be between `min` and `max`.
28292
28412
  function clamp(value, min, max) {
28293
28413
  if (value < min) {
@@ -28504,377 +28624,6 @@ function pixelsToVectors(pixels, graphState) {
28504
28624
  });
28505
28625
  }
28506
28626
 
28507
- function AxisLabels() {
28508
- const {
28509
- range,
28510
- labels,
28511
- width,
28512
- height
28513
- } = useGraphConfig();
28514
- const yAxisLabelLocation = [0, range[Y][MAX]];
28515
- const xAxisLabelLocation = [range[X][MAX], 0];
28516
- const [xAxisLabelText, yAxisLabelText] = labels;
28517
- const graphInfo = {
28518
- range,
28519
- width,
28520
- height
28521
- };
28522
- const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
28523
- // The default location for the y-axis-label is at the maximum y point
28524
- // This is just underneath the tick line for that point. The -24 moves the
28525
- // label up one grid square, so it sits on top of the graph
28526
- const [x2, y2] = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
28527
- const {
28528
- TeX
28529
- } = getDependencies();
28530
- return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
28531
- style: {
28532
- position: "absolute",
28533
- left: x1,
28534
- top: y1,
28535
- fontSize: "14px",
28536
- transform: "translate(7px, -50%)"
28537
- }
28538
- }, /*#__PURE__*/React__default.createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default.createElement("span", {
28539
- style: {
28540
- position: "absolute",
28541
- left: x2,
28542
- top: y2,
28543
- fontSize: "14px",
28544
- transform: "translate(-50%, 0px)"
28545
- }
28546
- }, /*#__PURE__*/React__default.createElement(TeX, null, yAxisLabelText)));
28547
- }
28548
-
28549
- // Universal padding for the axis ticks to ensure that the labels
28550
- // have enough space to render without overlapping the tick lines
28551
- const tickPadding = 5;
28552
- const AxisTickLabels = () => {
28553
- const graphConfig = useGraphConfig();
28554
- const {
28555
- tickStep,
28556
- range
28557
- } = graphConfig;
28558
- const [[xMin, xMax], [yMin, yMax]] = range;
28559
- const [xTickStep, yTickStep] = tickStep;
28560
- const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
28561
- const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
28562
- return /*#__PURE__*/React.createElement("div", {
28563
- className: "axis-tick-labels"
28564
- }, /*#__PURE__*/React.createElement(YGridAxis, {
28565
- axisTicks: yGridTicks,
28566
- graphConfig: graphConfig
28567
- }), /*#__PURE__*/React.createElement(XGridAxis, {
28568
- axisTicks: xGridTicks,
28569
- graphConfig: graphConfig
28570
- }));
28571
- };
28572
-
28573
- // Generate the necessary styles and ticks for the y-axis tick labels container
28574
- const YGridAxis = props => {
28575
- const {
28576
- axisTicks,
28577
- graphConfig
28578
- } = props;
28579
- const {
28580
- width,
28581
- height
28582
- } = graphConfig;
28583
-
28584
- // First get the label height by calculating the difference between the first two y-axis labels
28585
- const yTopPoint = pointToPixel([0, axisTicks[0]], graphConfig);
28586
- const ySecondPoint = pointToPixel([0, axisTicks[1]], graphConfig);
28587
- const yLabelHeight = ySecondPoint[1] - yTopPoint[1];
28588
-
28589
- // When rendering purely positive y-axis labels, we want to adjust the left position
28590
- // from the lowest calculated tick, as yMin may be negative
28591
- const yLowestTick = axisTicks[axisTicks.length - 1];
28592
- const yLeftAdjustment = Math.min(Math.max(yTopPoint[0] - tickPadding, 0), width);
28593
-
28594
- // We only want to render the 0 label on the y-axis if it is left of the grid
28595
- const yAxisIsLeftOfGrid = yLeftAdjustment === 0;
28596
-
28597
- // If the y-axis is right of the graph's viewbox/range,
28598
- // we want to adjust both the positioning and text alignment
28599
- const yAxisRightOfGrid = yLeftAdjustment === width;
28600
-
28601
- // Determine the default spacing for the labels based on whether the y-axis is right of the grid
28602
- const defaultSpacing = yAxisRightOfGrid ? 0.5 : 0;
28603
-
28604
- // We also want to adjust the spacing if there are no negative y-axis labels as the extra space is not needed
28605
- const marginSpacing = yLowestTick >= 0 ? defaultSpacing * 2 : defaultSpacing;
28606
-
28607
- // Determine the text alignment based on whether the y-axis is right of the grid
28608
- // We need to cast this as a const as it is used in the inline styles
28609
- const textAlign = yAxisRightOfGrid ? "left" : "right";
28610
- const yAxisStyles = {
28611
- left: `calc(${yLeftAdjustment}px - ${marginSpacing}em)`,
28612
- top: `calc(${yTopPoint[1]}px - 0.5em)`,
28613
- height,
28614
- textAlign,
28615
- "--y-axis-label-height": yLabelHeight + "px"
28616
- };
28617
- const classNames = yAxisRightOfGrid ? "y-axis-tick-labels y-axis-right-of-grid" : "y-axis-tick-labels ";
28618
- return /*#__PURE__*/React.createElement("div", {
28619
- className: classNames,
28620
- "data-testid": "y-axis-tick-labels",
28621
- style: yAxisStyles
28622
- }, axisTicks.map(label => {
28623
- return /*#__PURE__*/React.createElement(AxisTickLabel, {
28624
- label: label,
28625
- axis: "y",
28626
- key: `y-grid-tick-${label}`,
28627
- graphConfig: graphConfig,
28628
- axisOutOfBounds: yAxisIsLeftOfGrid
28629
- });
28630
- }));
28631
- };
28632
-
28633
- // Generate the necessary styles and ticks for the x-axis tick labels container
28634
- const XGridAxis = props => {
28635
- const {
28636
- axisTicks,
28637
- graphConfig
28638
- } = props;
28639
- const {
28640
- width,
28641
- height
28642
- } = graphConfig;
28643
-
28644
- // Determine label width by calculating the difference between the first two x-axis labels
28645
- const xFirstPoint = pointToPixel([axisTicks[0], 0], graphConfig);
28646
- const xSecondPoint = pointToPixel([axisTicks[1], 0], graphConfig);
28647
- const xLabelWidth = Math.abs(xSecondPoint[0] - xFirstPoint[0]);
28648
-
28649
- // Determine the top position of the x-axis labels based on
28650
- // whether the x-axis is above, within, or below the graph
28651
- const xAxisAboveGraph = xFirstPoint[1] < 0;
28652
- const xTopAdjustment = xAxisAboveGraph ? 0 : Math.min(xFirstPoint[1] + tickPadding, height);
28653
-
28654
- // Determine whether the x-axis is outside of the graph's viewbox/range
28655
- // This is used to conditionally render the 0 label on the x-axis
28656
- const xAxisOutOfBounds = xTopAdjustment === height || xAxisAboveGraph;
28657
-
28658
- // Determine the available space to the right of the x-axis labels
28659
- const availableSpaceRight = width - xFirstPoint[0];
28660
-
28661
- // Calculate the total combined width of ALL the x-axis labels
28662
- const axisTicksWidth = axisTicks.length * xLabelWidth;
28663
-
28664
- // Determine if we need to increase the width of the axis past the graph width
28665
- const calculatedWidth = axisTicksWidth > width ? axisTicksWidth : width;
28666
-
28667
- // Determine the right adjustment for the x-axis labels based on the available space
28668
- // We're adjusting the right as the flex-direction is row-reverse
28669
- const xRightAdjustment = availableSpaceRight - xLabelWidth / 2;
28670
- const xAxisStyles = {
28671
- top: xTopAdjustment,
28672
- right: xRightAdjustment,
28673
- width: calculatedWidth,
28674
- "--x-axis-label-width": xLabelWidth + "px"
28675
- };
28676
- return /*#__PURE__*/React.createElement("div", {
28677
- className: `x-axis-tick-labels ${xAxisAboveGraph && "x-axis-top-of-grid"}`,
28678
- "data-testid": "x-axis-tick-labels",
28679
- style: xAxisStyles
28680
- }, axisTicks.map(label => {
28681
- return /*#__PURE__*/React.createElement(AxisTickLabel, {
28682
- axis: "x",
28683
- label: label,
28684
- key: `x-grid-tick-${label}`,
28685
- graphConfig: graphConfig,
28686
- axisOutOfBounds: xAxisOutOfBounds
28687
- });
28688
- }));
28689
- };
28690
-
28691
- // Generate the individual tick label elements
28692
- const AxisTickLabel = ({
28693
- label,
28694
- graphConfig,
28695
- axisOutOfBounds,
28696
- axis
28697
- }) => {
28698
- const {
28699
- gridStep,
28700
- tickStep
28701
- } = graphConfig;
28702
- // Determine the point on the axis based on the axis type
28703
- const pointOnAxis = axis === "x" ? [label, 0] : [0, label];
28704
- const pixelPoint = pointToPixel(pointOnAxis, graphConfig);
28705
-
28706
- // Determine the relevant edge of the graph based on the axis type
28707
- const graphEdge = axis === "x" ? graphConfig.width : graphConfig.height;
28708
-
28709
- // Determine the vector index based on the axis type
28710
- const vectorIndex = axis === "x" ? 0 : 1;
28711
-
28712
- // Determine whether or not to render the tick label text
28713
- const shouldShowLabel = showTickLabel({
28714
- label,
28715
- axisOutOfBounds,
28716
- graphEdge,
28717
- position: pixelPoint[vectorIndex],
28718
- gridStep: gridStep[vectorIndex],
28719
- tickStep: tickStep[vectorIndex]
28720
- });
28721
- return /*#__PURE__*/React.createElement("span", {
28722
- className: `${axis}-axis-tick-label`
28723
- }, shouldShowLabel && label);
28724
- };
28725
-
28726
- // Determine whether or not to show the tick label text
28727
- const showTickLabel = ({
28728
- gridStep,
28729
- tickStep,
28730
- label,
28731
- graphEdge,
28732
- position,
28733
- axisOutOfBounds
28734
- }) => {
28735
- let showLabel = true;
28736
-
28737
- // We only want to show the initial negative tick labels (e.g. -1) on each
28738
- // axis if the tickStep > gridStep, to ensure that these labels do not overlap.
28739
- // e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
28740
- // which allows enough room for both these tick labels to render.
28741
- showLabel = tickStep > gridStep ? true : label !== -tickStep;
28742
-
28743
- // If the tick label position is greater than the relevant edge of the graph, don't render it
28744
- if (position <= -graphEdge || position >= graphEdge) {
28745
- showLabel = false;
28746
- }
28747
-
28748
- // If the label is 0, we only want to show it if the axis is to the left of the graph's viewbox/range
28749
- // otherwise it will overlap with graph axis labels
28750
- if (label === 0 && !axisOutOfBounds) {
28751
- showLabel = false;
28752
- }
28753
- return showLabel;
28754
- };
28755
-
28756
- // Generate the tick labels for an axis based on the tick step
28757
- function generateTickLocations$1(tickStep, min, max) {
28758
- const positiveTicks = [];
28759
- const negativeTicks = [];
28760
- const ticks = [];
28761
-
28762
- // Add 0 if it is applicable.
28763
- // This will be conditionally rendered using showTickLabel.
28764
- if (min <= 0 && max >= 0) {
28765
- positiveTicks.push(0);
28766
- }
28767
-
28768
- // Add ticks on the positive axis
28769
- // Start at the first tick after 0 or the minimum value,
28770
- // taking into account that the graph may be zoomed in on the positive axis
28771
- let i = Math.max(min, 0) + tickStep;
28772
- for (i; min < i && i < max; i += tickStep) {
28773
- positiveTicks.push(i);
28774
- }
28775
-
28776
- // Add ticks on the negative axis
28777
- // Start at the first tick before 0 or the maximum value,
28778
- // taking into account that the graph may be zoomed in on the negative axis
28779
- i = Math.min(max, 0) - tickStep;
28780
- for (i; i > min; i -= tickStep) {
28781
- negativeTicks.push(i);
28782
- }
28783
-
28784
- // Reverse the ticks so that they are in the correct render order
28785
- positiveTicks.reverse();
28786
- ticks.push(...positiveTicks, ...negativeTicks);
28787
- return ticks;
28788
- }
28789
-
28790
- function pathBuilder() {
28791
- return new PathBuilder();
28792
- }
28793
- class PathBuilder {
28794
- constructor() {
28795
- this.path = [];
28796
- this.scaleFactor = 1;
28797
- }
28798
- build() {
28799
- return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
28800
- }
28801
-
28802
- // add a move (M) command to the path
28803
- move(x, y) {
28804
- this.path.push({
28805
- action: "M",
28806
- args: [x, y]
28807
- });
28808
- return this;
28809
- }
28810
- line(x, y) {
28811
- this.path.push({
28812
- action: "L",
28813
- args: [x, y]
28814
- });
28815
- return this;
28816
- }
28817
- circularArc(radius, toX, toY, {
28818
- sweep = false,
28819
- largeArc = false
28820
- } = {}) {
28821
- this.path.push({
28822
- action: "A",
28823
- args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
28824
- });
28825
- return this;
28826
- }
28827
-
28828
- // add a curve (C) command to the path
28829
- curve(control1X, control1Y, control2X, control2Y, endX, endY) {
28830
- this.path.push({
28831
- action: "C",
28832
- args: [control1X, control1Y, control2X, control2Y, endX, endY]
28833
- });
28834
- return this;
28835
- }
28836
-
28837
- // Scale all coordinates of the path by a factor
28838
- scale(factor) {
28839
- this.scaleFactor *= factor;
28840
- return this;
28841
- }
28842
- }
28843
- function commandToString(command) {
28844
- return `${command.action}${command.args.join(" ")}`;
28845
- }
28846
- function scaleCommandBy(scaleFactor) {
28847
- return command => {
28848
- switch (command.action) {
28849
- case "A":
28850
- // TODO(benchristel): do we want to refactor this switch to
28851
- // use polymorphism?
28852
- // Arc command
28853
- return _extends({}, command, {
28854
- args: [command.args[0] * scaleFactor,
28855
- // x radius
28856
- command.args[1] * scaleFactor,
28857
- // y radius
28858
- command.args[2],
28859
- // rotation
28860
- command.args[3],
28861
- // largeArc flag
28862
- command.args[4],
28863
- // sweep flag
28864
- command.args[5] * scaleFactor,
28865
- // end x
28866
- command.args[6] * scaleFactor // end y
28867
- ]
28868
- });
28869
-
28870
- default:
28871
- return _extends({}, command, {
28872
- args: command.args.map(c => c * scaleFactor)
28873
- });
28874
- }
28875
- };
28876
- }
28877
-
28878
28627
  // We use the pathBuilder here to scale up the SVG path coordinates used
28879
28628
  // elsewhere for arrowheads. We scale in the path itself instead of using a CSS
28880
28629
  // transform because a transform would also scale up the stroke weight, and we
@@ -28905,7 +28654,10 @@ function AxisArrows() {
28905
28654
  range: [[xMin, xMax], [yMin, yMax]]
28906
28655
  } = useGraphConfig();
28907
28656
  const axisColor = "var(--mafs-fg)";
28908
- return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrowhead, {
28657
+
28658
+ // Only render the arrows if the axis is within the visible range
28659
+ // as otherwise the arrows will be rendered outside the graph
28660
+ return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, !(yMin > 0 || yMax < 0) && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrowhead, {
28909
28661
  color: axisColor,
28910
28662
  tip: [xMin, 0],
28911
28663
  angle: 180
@@ -28913,7 +28665,7 @@ function AxisArrows() {
28913
28665
  color: axisColor,
28914
28666
  tip: [xMax, 0],
28915
28667
  angle: 0
28916
- }), /*#__PURE__*/React__default.createElement(Arrowhead, {
28668
+ })), !(xMin > 0 || xMax < 0) && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrowhead, {
28917
28669
  color: axisColor,
28918
28670
  tip: [0, yMin],
28919
28671
  angle: 90
@@ -28921,38 +28673,75 @@ function AxisArrows() {
28921
28673
  color: axisColor,
28922
28674
  tip: [0, yMax],
28923
28675
  angle: 270
28924
- }));
28676
+ })));
28677
+ }
28678
+
28679
+ function AxisLabels() {
28680
+ const {
28681
+ range,
28682
+ labels,
28683
+ width,
28684
+ height
28685
+ } = useGraphConfig();
28686
+ const yAxisLabelLocation = [0, range[Y][MAX]];
28687
+ const xAxisLabelLocation = [range[X][MAX], 0];
28688
+ const [xAxisLabelText, yAxisLabelText] = labels;
28689
+ const graphInfo = {
28690
+ range,
28691
+ width,
28692
+ height
28693
+ };
28694
+ const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
28695
+ // The default location for the y-axis-label is at the maximum y point
28696
+ // This is just underneath the tick line for that point. The -24 moves the
28697
+ // label up one grid square, so it sits on top of the graph
28698
+ const [x2, y2] = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
28699
+ const {
28700
+ TeX
28701
+ } = getDependencies();
28702
+ return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
28703
+ style: {
28704
+ position: "absolute",
28705
+ left: x1,
28706
+ top: y1,
28707
+ fontSize: "14px",
28708
+ transform: "translate(7px, -50%)"
28709
+ }
28710
+ }, /*#__PURE__*/React__default.createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default.createElement("span", {
28711
+ style: {
28712
+ position: "absolute",
28713
+ left: x2,
28714
+ top: y2,
28715
+ fontSize: "14px",
28716
+ transform: "translate(-50%, 0px)"
28717
+ }
28718
+ }, /*#__PURE__*/React__default.createElement(TeX, null, yAxisLabelText)));
28925
28719
  }
28926
28720
 
28721
+ // The size of the ticks and labels in pixels
28927
28722
  const tickSize = 10;
28928
- const tickStyle = {
28929
- stroke: "black",
28930
- strokeWidth: 1
28931
- };
28723
+ const tickLabelSize = 14;
28932
28724
  const YGridTick = ({
28933
28725
  y,
28934
- graphInfo
28726
+ range
28935
28727
  }) => {
28728
+ // If the graph requires out-of-bounds labels, we want to make sure to set the
28729
+ // coordinates to the edge of the visible range of the graph. Otherwise,
28730
+ // the ticks and labels would render outside of the clipping-mask.
28936
28731
  let xPointOnAxis = 0;
28937
-
28938
- // If the graph is zoomed in, we want to make sure the ticks are still visible
28939
- // even if they are outside the graph's range.
28940
- if (graphInfo.range[X][MIN] > 0) {
28732
+ if (range[X][MIN] > 0) {
28941
28733
  // If the graph is on the positive side of the x-axis, lock the ticks to the left side of the graph
28942
- xPointOnAxis = graphInfo.range[X][MIN];
28734
+ xPointOnAxis = range[X][MIN];
28943
28735
  }
28944
- if (graphInfo.range[X][MAX] < 0) {
28736
+ if (range[X][MAX] < 0) {
28945
28737
  // If the graph is on the negative side of the x-axis, lock the ticks to the right side of the graph
28946
- xPointOnAxis = graphInfo.range[X][MAX];
28738
+ xPointOnAxis = range[X][MAX];
28947
28739
  }
28740
+
28741
+ // Convert the Vector2 coordinates to pixel coordinates
28948
28742
  const pointOnAxis = [xPointOnAxis, y];
28949
28743
  const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
28950
28744
 
28951
- // If the tick is on the edge of the graph's range, don't render it
28952
- if (yPosition === -graphInfo.height || yPosition === graphInfo.height + 20) {
28953
- return null;
28954
- }
28955
-
28956
28745
  // Position of the start of the tick
28957
28746
  const x1 = xPosition - tickSize / 2;
28958
28747
  const y1 = yPosition;
@@ -28960,39 +28749,55 @@ const YGridTick = ({
28960
28749
  // Position of the end of the tick
28961
28750
  const x2 = xPosition + tickSize / 2;
28962
28751
  const y2 = yPosition;
28752
+
28753
+ // Adjust the y position of the x-axis labels based on
28754
+ // whether the x-axis is above, within, or below the graph
28755
+ const xAdjustment = range[X][MAX] <= 0 ? tickLabelSize * 1.5 : -tickLabelSize * 1.1;
28756
+ const xPositionText = xPosition + xAdjustment;
28757
+ const yPositionText = yPosition + tickLabelSize * 0.25; // Center the text vertically on the tick
28758
+
28759
+ // If the graph displays both the y and x axis lines within the graph, we want
28760
+ // to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
28761
+ const showLabel = shouldShowLabel(y, range);
28963
28762
  return /*#__PURE__*/React.createElement("g", {
28964
- className: "y-axis-ticks"
28763
+ className: "tick"
28965
28764
  }, /*#__PURE__*/React.createElement("line", {
28966
28765
  x1: x1,
28967
28766
  y1: y1,
28968
28767
  x2: x2,
28969
28768
  y2: y2,
28970
- style: tickStyle
28971
- }));
28769
+ className: "axis-tick"
28770
+ }), showLabel && /*#__PURE__*/React.createElement("text", {
28771
+ className: "axis-tick-label",
28772
+ style: {
28773
+ fontSize: tickLabelSize
28774
+ },
28775
+ textAnchor: "end",
28776
+ x: xPositionText,
28777
+ y: yPositionText
28778
+ }, y.toString()));
28972
28779
  };
28973
28780
  const XGridTick = ({
28974
28781
  x,
28975
- graphInfo
28782
+ range
28976
28783
  }) => {
28784
+ // If the graph requires out-of-bounds labels, we want to make sure to set the
28785
+ // coordinates to the edge of the visible range of the graph. Otherwise,
28786
+ // the ticks and labels would render outside of the clipping-mask.
28977
28787
  let yPointOnAxis = 0;
28978
- // If the graph is zoomed in, we want to make sure the ticks are still visible
28979
- // even if they are outside the graph's range.
28980
- if (graphInfo.range[Y][MIN] > 0) {
28788
+ if (range[Y][MIN] > 0) {
28981
28789
  // If the graph is on the positive side of the y-axis, lock the ticks to the top of the graph
28982
- yPointOnAxis = graphInfo.range[Y][MIN];
28790
+ yPointOnAxis = range[Y][MIN];
28983
28791
  }
28984
- if (graphInfo.range[Y][MAX] < 0) {
28792
+ if (range[Y][MAX] < 0) {
28985
28793
  // If the graph is on the negative side of the x-axis, lock the ticks to the bottom of the graph
28986
- yPointOnAxis = graphInfo.range[Y][MAX];
28794
+ yPointOnAxis = range[Y][MAX];
28987
28795
  }
28796
+
28797
+ // Convert the Vector2 coordinates to pixel coordinates
28988
28798
  const pointOnAxis = [x, yPointOnAxis];
28989
28799
  const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
28990
28800
 
28991
- // If the tick is on the edge of the graph's range, don't render it
28992
- if (xPosition === -graphInfo.width / 2 || xPosition === graphInfo.width) {
28993
- return null;
28994
- }
28995
-
28996
28801
  // Position of the start of the tick
28997
28802
  const x1 = xPosition;
28998
28803
  const y1 = yPosition + tickSize / 2;
@@ -29000,21 +28805,56 @@ const XGridTick = ({
29000
28805
  // Position of the end of the tick
29001
28806
  const x2 = xPosition;
29002
28807
  const y2 = yPosition - tickSize / 2;
28808
+
28809
+ // Adjust the Y position of the x-axis labels based on
28810
+ // whether the x-axis is above, within, or below the graph
28811
+ const yAdjustment = range[Y][MAX] < 0 ? -tickLabelSize : tickLabelSize * 1.75;
28812
+
28813
+ // Adjust the X position of the x-axis labels based on
28814
+ // whether the label is positive or negative, in order to
28815
+ // account for the width of the negative sign
28816
+ const xAdjustment = x < 0 ? -2 : 0;
28817
+
28818
+ // Apply the adjustments to the x and y positions for the text
28819
+ const xPositionText = xPosition + xAdjustment;
28820
+ const yPositionText = yPosition + yAdjustment;
29003
28821
  return /*#__PURE__*/React.createElement("g", {
29004
- className: "x-axis-ticks"
28822
+ className: "tick"
29005
28823
  }, /*#__PURE__*/React.createElement("line", {
29006
28824
  x1: x1,
29007
28825
  y1: y1,
29008
28826
  x2: x2,
29009
28827
  y2: y2,
29010
- style: tickStyle
29011
- }));
28828
+ className: "axis-tick"
28829
+ }), /*#__PURE__*/React.createElement("text", {
28830
+ className: "axis-tick-label",
28831
+ style: {
28832
+ fontSize: tickLabelSize
28833
+ },
28834
+ textAnchor: "middle",
28835
+ x: xPositionText,
28836
+ y: yPositionText
28837
+ }, x.toString()));
28838
+ };
28839
+
28840
+ // Determines whether to show the label for the given tick
28841
+ // Currently, the only condition is to hide the label at -1
28842
+ // on the y-axis when the x-axis is within the graph
28843
+ const shouldShowLabel = (number, range) => {
28844
+ let showLabel = true;
28845
+
28846
+ // If the x-axis is within the graph and the y-axis is at -1, hide the label
28847
+ if (range[X][MIN] < -1 && range[X][MAX] > 0 && number === -1) {
28848
+ showLabel = false;
28849
+ }
28850
+ return showLabel;
29012
28851
  };
29013
28852
  function generateTickLocations(tickStep, min, max) {
29014
28853
  const ticks = [];
29015
28854
 
29016
28855
  // Add ticks in the positive direction
29017
- for (let _i = 0 + tickStep; _i < max; _i += tickStep) {
28856
+ const start = Math.max(min, 0);
28857
+ for (let _i = start + tickStep; _i < max; _i += tickStep) {
29018
28858
  ticks.push(_i);
29019
28859
  }
29020
28860
 
@@ -29029,34 +28869,34 @@ function generateTickLocations(tickStep, min, max) {
29029
28869
  const AxisTicks = () => {
29030
28870
  const {
29031
28871
  tickStep,
29032
- range,
29033
- width,
29034
- height
28872
+ range
29035
28873
  } = useGraphConfig();
29036
- const graphInfo = {
29037
- range,
29038
- width,
29039
- height
29040
- };
29041
28874
  const [[xMin, xMax], [yMin, yMax]] = range;
29042
28875
  const [xTickStep, yTickStep] = tickStep;
28876
+
28877
+ // Generate the tick locations & labels for the x and y axes
29043
28878
  const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
29044
28879
  const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
29045
28880
  return /*#__PURE__*/React.createElement("g", {
29046
- className: "axis-ticks"
28881
+ className: "axis-ticks",
28882
+ role: "presentation"
28883
+ }, /*#__PURE__*/React.createElement("g", {
28884
+ className: "y-axis-ticks"
29047
28885
  }, yGridTicks.map(y => {
29048
28886
  return /*#__PURE__*/React.createElement(YGridTick, {
29049
28887
  y: y,
29050
28888
  key: `y-grid-tick-${y}`,
29051
- graphInfo: graphInfo
28889
+ range: range
29052
28890
  });
29053
- }), xGridTicks.map(x => {
28891
+ })), /*#__PURE__*/React.createElement("g", {
28892
+ className: "x-axis-ticks"
28893
+ }, xGridTicks.map(x => {
29054
28894
  return /*#__PURE__*/React.createElement(XGridTick, {
29055
28895
  x: x,
29056
28896
  key: `x-grid-tick-${x}`,
29057
- graphInfo: graphInfo
28897
+ range: range
29058
28898
  });
29059
- }));
28899
+ })));
29060
28900
  };
29061
28901
 
29062
28902
  /**
@@ -29077,12 +28917,10 @@ const axisOptions = (props, axisIndex) => {
29077
28917
  };
29078
28918
  };
29079
28919
  const Grid = props => {
29080
- return props.markings === "none" ? null : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Coordinates.Cartesian, {
28920
+ return props.markings === "none" ? null : /*#__PURE__*/React.createElement(Coordinates.Cartesian, {
29081
28921
  xAxis: axisOptions(props, X),
29082
28922
  yAxis: axisOptions(props, Y)
29083
- }),
29084
- // Only render the axis ticks and arrows if the markings are set to a full "graph"
29085
- props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)));
28923
+ });
29086
28924
  };
29087
28925
 
29088
28926
  // Displays a stylized open eye: lint warnings are visible
@@ -31994,11 +31832,31 @@ const MafsGraph = props => {
31994
31832
  state,
31995
31833
  dispatch,
31996
31834
  labels,
31997
- readOnly
31835
+ readOnly,
31836
+ fullGraphAriaLabel,
31837
+ fullGraphAriaDescription
31998
31838
  } = props;
31999
31839
  const [width, height] = props.box;
32000
31840
  const tickStep = props.step;
31841
+ const uniqueId = React.useId();
31842
+ const descriptionId = `interactive-graph-description-${uniqueId}`;
32001
31843
  const graphRef = React.useRef(null);
31844
+
31845
+ // Set up the SVG attributes for the nested SVGs that help lock
31846
+ // the grid and graph elements to the bounds of the graph.
31847
+ const {
31848
+ viewboxX,
31849
+ viewboxY
31850
+ } = calculateNestedSVGCoords(state.range, width, height);
31851
+ const viewBox = `${viewboxX} ${viewboxY} ${width} ${height}`;
31852
+ const nestedSVGAttributes = {
31853
+ width,
31854
+ height,
31855
+ viewBox,
31856
+ preserveAspectRatio: "xMidYMin",
31857
+ x: viewboxX,
31858
+ y: viewboxY
31859
+ };
32002
31860
  return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
32003
31861
  value: {
32004
31862
  range: state.range,
@@ -32020,7 +31878,7 @@ const MafsGraph = props => {
32020
31878
  padding: "25px 25px 0 0",
32021
31879
  boxSizing: "content-box",
32022
31880
  marginLeft: "20px",
32023
- marginBottom: "20px",
31881
+ marginBottom: "30px",
32024
31882
  pointerEvents: props.static ? "none" : "auto",
32025
31883
  userSelect: "none",
32026
31884
  width,
@@ -32033,9 +31891,19 @@ const MafsGraph = props => {
32033
31891
  (_graphRef$current = graphRef.current) == null || _graphRef$current.focus();
32034
31892
  }
32035
31893
  },
31894
+ "aria-label": fullGraphAriaLabel,
31895
+ "aria-describedby": fullGraphAriaDescription ? descriptionId : undefined,
32036
31896
  ref: graphRef,
32037
31897
  tabIndex: 0
32038
- }, /*#__PURE__*/React.createElement(LegacyGrid, {
31898
+ }, fullGraphAriaDescription && /*#__PURE__*/React.createElement(View, {
31899
+ id: descriptionId,
31900
+ tabIndex: -1,
31901
+ style: {
31902
+ width: 0,
31903
+ height: 0,
31904
+ overflow: "hidden"
31905
+ }
31906
+ }, fullGraphAriaDescription), /*#__PURE__*/React.createElement(LegacyGrid, {
32039
31907
  box: props.box,
32040
31908
  backgroundImage: props.backgroundImage
32041
31909
  }), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
@@ -32046,7 +31914,7 @@ const MafsGraph = props => {
32046
31914
  bottom: 0,
32047
31915
  left: 0
32048
31916
  }
32049
- }, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(AxisTickLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
31917
+ }, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
32050
31918
  preserveAspectRatio: false,
32051
31919
  viewBox: {
32052
31920
  x: state.range[X],
@@ -32057,19 +31925,22 @@ const MafsGraph = props => {
32057
31925
  zoom: false,
32058
31926
  width: width,
32059
31927
  height: height
32060
- }, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid, {
32061
- tickStep: props.step,
31928
+ }, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, /*#__PURE__*/React.createElement(Grid, {
32062
31929
  gridStep: props.gridStep,
32063
31930
  range: state.range,
32064
31931
  containerSizeClass: props.containerSizeClass,
32065
- markings: props.markings
32066
- }), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
31932
+ markings: props.markings,
31933
+ width: width,
31934
+ height: height
31935
+ })),
31936
+ // Only render the axis ticks and arrows if the markings are set to a full "graph"
31937
+ props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
32067
31938
  lockedFigures: props.lockedFigures,
32068
31939
  range: state.range
32069
31940
  }), props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
32070
31941
  state,
32071
31942
  dispatch
32072
- })))), renderGraphControls({
31943
+ }))))), renderGraphControls({
32073
31944
  state,
32074
31945
  dispatch
32075
31946
  })));
@@ -32105,6 +31976,56 @@ const renderGraphControls = props => {
32105
31976
  return null;
32106
31977
  }
32107
31978
  };
31979
+
31980
+ // Calculate the difference between the min and max values of a range
31981
+ const getRangeDiff = range => {
31982
+ const [min, max] = range;
31983
+ return Math.abs(max - min);
31984
+ };
31985
+
31986
+ // We need to adjust the nested SVG viewbox x and Y values based on the range of the graph in order
31987
+ // to ensure that the graph is sized and positioned correctly within the Mafs SVG and the clipping mask.
31988
+ // Exported for testing.
31989
+ const calculateNestedSVGCoords = (range, width, height) => {
31990
+ // X RANGE
31991
+ let viewboxX = 0; // When xMin is 0, we want to use 0 as the viewboxX value
31992
+ const totalXRange = getRangeDiff(range[X]);
31993
+ const gridCellWidth = width / totalXRange;
31994
+ const minX = range[X][MIN];
31995
+
31996
+ // If xMin is entirely positive, we need to adjust the
31997
+ // viewboxX to be the grid cell width multiplied by xMin
31998
+ if (minX > 0) {
31999
+ viewboxX = gridCellWidth * Math.abs(minX);
32000
+ }
32001
+ // If xMin is negative, we need to adjust the viewboxX to be
32002
+ // the negative value of the grid cell width multiplied by xMin
32003
+ if (minX < 0) {
32004
+ viewboxX = -gridCellWidth * Math.abs(minX);
32005
+ }
32006
+
32007
+ // Y RANGE
32008
+ let viewboxY = -height; // When yMin is 0, we want to use the negative value of the graph height
32009
+ const totalYRange = getRangeDiff(range[Y]);
32010
+ const gridCellHeight = height / totalYRange;
32011
+ const minY = range[Y][MIN];
32012
+
32013
+ // If the y range is entirely positive, we want a negative sum of the
32014
+ // height and the gridcell height multiplied by the absolute value of yMin
32015
+ if (minY > 0) {
32016
+ viewboxY = -height - gridCellHeight * Math.abs(minY);
32017
+ }
32018
+
32019
+ // If the yMin is negative, we want to multiply the gridcell height
32020
+ // by the absolute value of yMin, and subtract the full height of the graph
32021
+ if (minY < 0) {
32022
+ viewboxY = gridCellHeight * Math.abs(minY) - height;
32023
+ }
32024
+ return {
32025
+ viewboxX,
32026
+ viewboxY
32027
+ };
32028
+ };
32108
32029
  const renderGraph = props => {
32109
32030
  const {
32110
32031
  state,
@@ -44695,7 +44616,7 @@ const init = function init(options) {
44695
44616
 
44696
44617
  // This file is processed by a Rollup plugin (replace) to inject the production
44697
44618
  const libName = "@khanacademy/perseus";
44698
- const libVersion = "32.0.1";
44619
+ const libVersion = "33.1.0";
44699
44620
  addLibraryVersionToPerseusDebug(libName, libVersion);
44700
44621
 
44701
44622
  const apiVersion = {
@@ -45579,128 +45500,6 @@ const ref = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.cre
45579
45500
  ref: ref
45580
45501
  }))));
45581
45502
 
45582
- class ArrowPicker extends React.Component {
45583
- render() {
45584
- return /*#__PURE__*/React.createElement(ButtonGroup, {
45585
- value: this.props.value,
45586
- allowEmpty: false,
45587
- buttons: [{
45588
- value: "",
45589
- content: /*#__PURE__*/React.createElement("span", null, "\u2014")
45590
- }, {
45591
- value: "->",
45592
- content: /*#__PURE__*/React.createElement("span", null, "\u2192")
45593
- }
45594
- /*
45595
- TODO(eater): fix khan-exercises so these are supported
45596
- {value: "<-", content: <span>&#x2190;</span>},
45597
- {value: "<->", content: <span>&#x2194;</span>}
45598
- */],
45599
-
45600
- onChange: this.props.onChange
45601
- });
45602
- }
45603
- }
45604
- ArrowPicker.defaultProps = {
45605
- value: ""
45606
- };
45607
-
45608
- const COLORS = [KhanColors.BLACK, KhanColors.BLUE, KhanColors.GREEN, KhanColors.PINK, KhanColors.PURPLE, KhanColors.RED, KhanColors.GRAY];
45609
- const LIGHT_COLORS = [KhanColors.LIGHT_BLUE, KhanColors.LIGHT_ORANGE, KhanColors.LIGHT_PINK, KhanColors.LIGHT_GREEN, KhanColors.LIGHT_PURPLE, KhanColors.LIGHT_RED, "#fff"];
45610
- class ColorPicker extends React.Component {
45611
- render() {
45612
- const colors = this.props.lightColors ? LIGHT_COLORS : COLORS;
45613
- return /*#__PURE__*/React.createElement(ButtonGroup, {
45614
- value: this.props.value,
45615
- allowEmpty: false,
45616
- buttons: _.map(colors, color => {
45617
- return {
45618
- value: color,
45619
- content: /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("span", {
45620
- className: "colorpicker-circle",
45621
- style: {
45622
- background: color
45623
- }
45624
- }), "\xA0")
45625
- };
45626
- }),
45627
- onChange: this.props.onChange
45628
- });
45629
- }
45630
- }
45631
- ColorPicker.defaultProps = {
45632
- value: KhanColors.BLACK,
45633
- lightColors: false
45634
- };
45635
-
45636
- class DashPicker extends React.Component {
45637
- render() {
45638
- return /*#__PURE__*/React.createElement(ButtonGroup, {
45639
- value: this.props.value,
45640
- allowEmpty: false,
45641
- buttons: [{
45642
- value: "",
45643
- content: /*#__PURE__*/React.createElement("span", null, "\u2014")
45644
- }, {
45645
- value: "-",
45646
- content: /*#__PURE__*/React.createElement("span", null, "\u2013\u2013\u2013")
45647
- }, {
45648
- value: "- ",
45649
- content: /*#__PURE__*/React.createElement("span", null, "\u2013\xA0\xA0\u2013")
45650
- }, {
45651
- value: ".",
45652
- content: /*#__PURE__*/React.createElement("span", null, "\xB7\xB7\xB7\xB7")
45653
- }, {
45654
- value: ". ",
45655
- content: /*#__PURE__*/React.createElement("span", null, "\xB7 \xB7 \xB7")
45656
- }],
45657
- onChange: this.props.onChange
45658
- });
45659
- }
45660
- }
45661
- DashPicker.defaultProps = {
45662
- value: ""
45663
- };
45664
-
45665
- /* eslint-disable jsx-a11y/anchor-is-valid */
45666
- class ElementContainer extends React.Component {
45667
- constructor(props) {
45668
- super(props);
45669
- this.toggle = e => {
45670
- e.preventDefault();
45671
- this.setState({
45672
- show: !this.state.show
45673
- });
45674
- };
45675
- this.state = {
45676
- show: props.initiallyVisible
45677
- };
45678
- }
45679
- render() {
45680
- return /*#__PURE__*/React.createElement("div", {
45681
- className: "perseus-interaction-element"
45682
- }, /*#__PURE__*/React.createElement("a", {
45683
- href: "#",
45684
- className: "perseus-interaction-element-title " + (this.state.show ? "open" : "closed"),
45685
- onClick: this.toggle
45686
- }, this.state.show ? /*#__PURE__*/React.createElement(InlineIcon, iconChevronDown) : /*#__PURE__*/React.createElement(InlineIcon, iconChevronRight), this.props.title), /*#__PURE__*/React.createElement("div", {
45687
- className: "perseus-interaction-element-content " + (this.state.show ? "enter" : "leave")
45688
- }, this.props.children, (this.props.onUp != null || this.props.onDown != null || this.props.onDelete != null) && /*#__PURE__*/React.createElement("div", {
45689
- className: "edit-controls"
45690
- }, this.props.onUp != null && /*#__PURE__*/React.createElement("button", {
45691
- onClick: this.props.onUp
45692
- }, /*#__PURE__*/React.createElement(InlineIcon, iconCircleArrowUp)), this.props.onDown != null && /*#__PURE__*/React.createElement("button", {
45693
- onClick: this.props.onDown
45694
- }, /*#__PURE__*/React.createElement(InlineIcon, iconCircleArrowDown)), this.props.onDelete != null && /*#__PURE__*/React.createElement("button", {
45695
- onClick: this.props.onDelete
45696
- }, /*#__PURE__*/React.createElement(InlineIcon, iconTrash)))));
45697
- }
45698
- }
45699
- ElementContainer.defaultProps = {
45700
- initiallyVisible: false,
45701
- title: "More"
45702
- };
45703
-
45704
45503
  const devices = {
45705
45504
  PHONE: "phone",
45706
45505
  TABLET: "tablet",
@@ -48280,5 +48079,5 @@ const EditorJsonify = {
48280
48079
  }
48281
48080
  };
48282
48081
 
48283
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
48082
+ export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
48284
48083
  //# sourceMappingURL=index.js.map