@khanacademy/perseus 33.0.0 → 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.css +15 -44
- package/dist/es/index.js +310 -435
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +15 -44
- package/dist/index.js +311 -438
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +0 -7
- package/dist/server-item-renderer.d.ts +0 -7
- package/dist/types.d.ts +1 -4
- package/dist/widgets/expression/expression.d.ts +0 -42
- package/dist/widgets/interactive-graphs/backgrounds/axis-ticks.d.ts +2 -0
- package/dist/widgets/interactive-graphs/backgrounds/grid.d.ts +2 -10
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +5 -0
- package/dist/widgets/label-image/label-image.d.ts +0 -14
- package/package.json +1 -1
- package/dist/widgets/interactive-graphs/backgrounds/axis-tick-labels.d.ts +0 -13
package/dist/es/index.js
CHANGED
|
@@ -28320,6 +28320,94 @@ Graph.defaultProps = {
|
|
|
28320
28320
|
isMobile: false
|
|
28321
28321
|
};
|
|
28322
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
|
+
|
|
28323
28411
|
// Restricts `value` to be between `min` and `max`.
|
|
28324
28412
|
function clamp(value, min, max) {
|
|
28325
28413
|
if (value < min) {
|
|
@@ -28536,377 +28624,6 @@ function pixelsToVectors(pixels, graphState) {
|
|
|
28536
28624
|
});
|
|
28537
28625
|
}
|
|
28538
28626
|
|
|
28539
|
-
function AxisLabels() {
|
|
28540
|
-
const {
|
|
28541
|
-
range,
|
|
28542
|
-
labels,
|
|
28543
|
-
width,
|
|
28544
|
-
height
|
|
28545
|
-
} = useGraphConfig();
|
|
28546
|
-
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28547
|
-
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28548
|
-
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
28549
|
-
const graphInfo = {
|
|
28550
|
-
range,
|
|
28551
|
-
width,
|
|
28552
|
-
height
|
|
28553
|
-
};
|
|
28554
|
-
const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
|
|
28555
|
-
// The default location for the y-axis-label is at the maximum y point
|
|
28556
|
-
// This is just underneath the tick line for that point. The -24 moves the
|
|
28557
|
-
// label up one grid square, so it sits on top of the graph
|
|
28558
|
-
const [x2, y2] = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
|
|
28559
|
-
const {
|
|
28560
|
-
TeX
|
|
28561
|
-
} = getDependencies();
|
|
28562
|
-
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
|
|
28563
|
-
style: {
|
|
28564
|
-
position: "absolute",
|
|
28565
|
-
left: x1,
|
|
28566
|
-
top: y1,
|
|
28567
|
-
fontSize: "14px",
|
|
28568
|
-
transform: "translate(7px, -50%)"
|
|
28569
|
-
}
|
|
28570
|
-
}, /*#__PURE__*/React__default.createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default.createElement("span", {
|
|
28571
|
-
style: {
|
|
28572
|
-
position: "absolute",
|
|
28573
|
-
left: x2,
|
|
28574
|
-
top: y2,
|
|
28575
|
-
fontSize: "14px",
|
|
28576
|
-
transform: "translate(-50%, 0px)"
|
|
28577
|
-
}
|
|
28578
|
-
}, /*#__PURE__*/React__default.createElement(TeX, null, yAxisLabelText)));
|
|
28579
|
-
}
|
|
28580
|
-
|
|
28581
|
-
// Universal padding for the axis ticks to ensure that the labels
|
|
28582
|
-
// have enough space to render without overlapping the tick lines
|
|
28583
|
-
const tickPadding = 5;
|
|
28584
|
-
const AxisTickLabels = () => {
|
|
28585
|
-
const graphConfig = useGraphConfig();
|
|
28586
|
-
const {
|
|
28587
|
-
tickStep,
|
|
28588
|
-
range
|
|
28589
|
-
} = graphConfig;
|
|
28590
|
-
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
28591
|
-
const [xTickStep, yTickStep] = tickStep;
|
|
28592
|
-
const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
|
|
28593
|
-
const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
|
|
28594
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
28595
|
-
className: "axis-tick-labels"
|
|
28596
|
-
}, /*#__PURE__*/React.createElement(YGridAxis, {
|
|
28597
|
-
axisTicks: yGridTicks,
|
|
28598
|
-
graphConfig: graphConfig
|
|
28599
|
-
}), /*#__PURE__*/React.createElement(XGridAxis, {
|
|
28600
|
-
axisTicks: xGridTicks,
|
|
28601
|
-
graphConfig: graphConfig
|
|
28602
|
-
}));
|
|
28603
|
-
};
|
|
28604
|
-
|
|
28605
|
-
// Generate the necessary styles and ticks for the y-axis tick labels container
|
|
28606
|
-
const YGridAxis = props => {
|
|
28607
|
-
const {
|
|
28608
|
-
axisTicks,
|
|
28609
|
-
graphConfig
|
|
28610
|
-
} = props;
|
|
28611
|
-
const {
|
|
28612
|
-
width,
|
|
28613
|
-
height
|
|
28614
|
-
} = graphConfig;
|
|
28615
|
-
|
|
28616
|
-
// First get the label height by calculating the difference between the first two y-axis labels
|
|
28617
|
-
const yTopPoint = pointToPixel([0, axisTicks[0]], graphConfig);
|
|
28618
|
-
const ySecondPoint = pointToPixel([0, axisTicks[1]], graphConfig);
|
|
28619
|
-
const yLabelHeight = ySecondPoint[1] - yTopPoint[1];
|
|
28620
|
-
|
|
28621
|
-
// When rendering purely positive y-axis labels, we want to adjust the left position
|
|
28622
|
-
// from the lowest calculated tick, as yMin may be negative
|
|
28623
|
-
const yLowestTick = axisTicks[axisTicks.length - 1];
|
|
28624
|
-
const yLeftAdjustment = Math.min(Math.max(yTopPoint[0] - tickPadding, 0), width);
|
|
28625
|
-
|
|
28626
|
-
// We only want to render the 0 label on the y-axis if it is left of the grid
|
|
28627
|
-
const yAxisIsLeftOfGrid = yLeftAdjustment === 0;
|
|
28628
|
-
|
|
28629
|
-
// If the y-axis is right of the graph's viewbox/range,
|
|
28630
|
-
// we want to adjust both the positioning and text alignment
|
|
28631
|
-
const yAxisRightOfGrid = yLeftAdjustment === width;
|
|
28632
|
-
|
|
28633
|
-
// Determine the default spacing for the labels based on whether the y-axis is right of the grid
|
|
28634
|
-
const defaultSpacing = yAxisRightOfGrid ? 0.5 : 0;
|
|
28635
|
-
|
|
28636
|
-
// We also want to adjust the spacing if there are no negative y-axis labels as the extra space is not needed
|
|
28637
|
-
const marginSpacing = yLowestTick >= 0 ? defaultSpacing * 2 : defaultSpacing;
|
|
28638
|
-
|
|
28639
|
-
// Determine the text alignment based on whether the y-axis is right of the grid
|
|
28640
|
-
// We need to cast this as a const as it is used in the inline styles
|
|
28641
|
-
const textAlign = yAxisRightOfGrid ? "left" : "right";
|
|
28642
|
-
const yAxisStyles = {
|
|
28643
|
-
left: `calc(${yLeftAdjustment}px - ${marginSpacing}em)`,
|
|
28644
|
-
top: `calc(${yTopPoint[1]}px - 0.5em)`,
|
|
28645
|
-
height,
|
|
28646
|
-
textAlign,
|
|
28647
|
-
"--y-axis-label-height": yLabelHeight + "px"
|
|
28648
|
-
};
|
|
28649
|
-
const classNames = yAxisRightOfGrid ? "y-axis-tick-labels y-axis-right-of-grid" : "y-axis-tick-labels ";
|
|
28650
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
28651
|
-
className: classNames,
|
|
28652
|
-
"data-testid": "y-axis-tick-labels",
|
|
28653
|
-
style: yAxisStyles
|
|
28654
|
-
}, axisTicks.map(label => {
|
|
28655
|
-
return /*#__PURE__*/React.createElement(AxisTickLabel, {
|
|
28656
|
-
label: label,
|
|
28657
|
-
axis: "y",
|
|
28658
|
-
key: `y-grid-tick-${label}`,
|
|
28659
|
-
graphConfig: graphConfig,
|
|
28660
|
-
axisOutOfBounds: yAxisIsLeftOfGrid
|
|
28661
|
-
});
|
|
28662
|
-
}));
|
|
28663
|
-
};
|
|
28664
|
-
|
|
28665
|
-
// Generate the necessary styles and ticks for the x-axis tick labels container
|
|
28666
|
-
const XGridAxis = props => {
|
|
28667
|
-
const {
|
|
28668
|
-
axisTicks,
|
|
28669
|
-
graphConfig
|
|
28670
|
-
} = props;
|
|
28671
|
-
const {
|
|
28672
|
-
width,
|
|
28673
|
-
height
|
|
28674
|
-
} = graphConfig;
|
|
28675
|
-
|
|
28676
|
-
// Determine label width by calculating the difference between the first two x-axis labels
|
|
28677
|
-
const xFirstPoint = pointToPixel([axisTicks[0], 0], graphConfig);
|
|
28678
|
-
const xSecondPoint = pointToPixel([axisTicks[1], 0], graphConfig);
|
|
28679
|
-
const xLabelWidth = Math.abs(xSecondPoint[0] - xFirstPoint[0]);
|
|
28680
|
-
|
|
28681
|
-
// Determine the top position of the x-axis labels based on
|
|
28682
|
-
// whether the x-axis is above, within, or below the graph
|
|
28683
|
-
const xAxisAboveGraph = xFirstPoint[1] < 0;
|
|
28684
|
-
const xTopAdjustment = xAxisAboveGraph ? 0 : Math.min(xFirstPoint[1] + tickPadding, height);
|
|
28685
|
-
|
|
28686
|
-
// Determine whether the x-axis is outside of the graph's viewbox/range
|
|
28687
|
-
// This is used to conditionally render the 0 label on the x-axis
|
|
28688
|
-
const xAxisOutOfBounds = xTopAdjustment === height || xAxisAboveGraph;
|
|
28689
|
-
|
|
28690
|
-
// Determine the available space to the right of the x-axis labels
|
|
28691
|
-
const availableSpaceRight = width - xFirstPoint[0];
|
|
28692
|
-
|
|
28693
|
-
// Calculate the total combined width of ALL the x-axis labels
|
|
28694
|
-
const axisTicksWidth = axisTicks.length * xLabelWidth;
|
|
28695
|
-
|
|
28696
|
-
// Determine if we need to increase the width of the axis past the graph width
|
|
28697
|
-
const calculatedWidth = axisTicksWidth > width ? axisTicksWidth : width;
|
|
28698
|
-
|
|
28699
|
-
// Determine the right adjustment for the x-axis labels based on the available space
|
|
28700
|
-
// We're adjusting the right as the flex-direction is row-reverse
|
|
28701
|
-
const xRightAdjustment = availableSpaceRight - xLabelWidth / 2;
|
|
28702
|
-
const xAxisStyles = {
|
|
28703
|
-
top: xTopAdjustment,
|
|
28704
|
-
right: xRightAdjustment,
|
|
28705
|
-
width: calculatedWidth,
|
|
28706
|
-
"--x-axis-label-width": xLabelWidth + "px"
|
|
28707
|
-
};
|
|
28708
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
28709
|
-
className: `x-axis-tick-labels ${xAxisAboveGraph && "x-axis-top-of-grid"}`,
|
|
28710
|
-
"data-testid": "x-axis-tick-labels",
|
|
28711
|
-
style: xAxisStyles
|
|
28712
|
-
}, axisTicks.map(label => {
|
|
28713
|
-
return /*#__PURE__*/React.createElement(AxisTickLabel, {
|
|
28714
|
-
axis: "x",
|
|
28715
|
-
label: label,
|
|
28716
|
-
key: `x-grid-tick-${label}`,
|
|
28717
|
-
graphConfig: graphConfig,
|
|
28718
|
-
axisOutOfBounds: xAxisOutOfBounds
|
|
28719
|
-
});
|
|
28720
|
-
}));
|
|
28721
|
-
};
|
|
28722
|
-
|
|
28723
|
-
// Generate the individual tick label elements
|
|
28724
|
-
const AxisTickLabel = ({
|
|
28725
|
-
label,
|
|
28726
|
-
graphConfig,
|
|
28727
|
-
axisOutOfBounds,
|
|
28728
|
-
axis
|
|
28729
|
-
}) => {
|
|
28730
|
-
const {
|
|
28731
|
-
gridStep,
|
|
28732
|
-
tickStep
|
|
28733
|
-
} = graphConfig;
|
|
28734
|
-
// Determine the point on the axis based on the axis type
|
|
28735
|
-
const pointOnAxis = axis === "x" ? [label, 0] : [0, label];
|
|
28736
|
-
const pixelPoint = pointToPixel(pointOnAxis, graphConfig);
|
|
28737
|
-
|
|
28738
|
-
// Determine the relevant edge of the graph based on the axis type
|
|
28739
|
-
const graphEdge = axis === "x" ? graphConfig.width : graphConfig.height;
|
|
28740
|
-
|
|
28741
|
-
// Determine the vector index based on the axis type
|
|
28742
|
-
const vectorIndex = axis === "x" ? 0 : 1;
|
|
28743
|
-
|
|
28744
|
-
// Determine whether or not to render the tick label text
|
|
28745
|
-
const shouldShowLabel = showTickLabel({
|
|
28746
|
-
label,
|
|
28747
|
-
axisOutOfBounds,
|
|
28748
|
-
graphEdge,
|
|
28749
|
-
position: pixelPoint[vectorIndex],
|
|
28750
|
-
gridStep: gridStep[vectorIndex],
|
|
28751
|
-
tickStep: tickStep[vectorIndex]
|
|
28752
|
-
});
|
|
28753
|
-
return /*#__PURE__*/React.createElement("span", {
|
|
28754
|
-
className: `${axis}-axis-tick-label`
|
|
28755
|
-
}, shouldShowLabel && label);
|
|
28756
|
-
};
|
|
28757
|
-
|
|
28758
|
-
// Determine whether or not to show the tick label text
|
|
28759
|
-
const showTickLabel = ({
|
|
28760
|
-
gridStep,
|
|
28761
|
-
tickStep,
|
|
28762
|
-
label,
|
|
28763
|
-
graphEdge,
|
|
28764
|
-
position,
|
|
28765
|
-
axisOutOfBounds
|
|
28766
|
-
}) => {
|
|
28767
|
-
let showLabel = true;
|
|
28768
|
-
|
|
28769
|
-
// We only want to show the initial negative tick labels (e.g. -1) on each
|
|
28770
|
-
// axis if the tickStep > gridStep, to ensure that these labels do not overlap.
|
|
28771
|
-
// e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
|
|
28772
|
-
// which allows enough room for both these tick labels to render.
|
|
28773
|
-
showLabel = tickStep > gridStep ? true : label !== -tickStep;
|
|
28774
|
-
|
|
28775
|
-
// If the tick label position is greater than the relevant edge of the graph, don't render it
|
|
28776
|
-
if (position <= -graphEdge || position >= graphEdge) {
|
|
28777
|
-
showLabel = false;
|
|
28778
|
-
}
|
|
28779
|
-
|
|
28780
|
-
// If the label is 0, we only want to show it if the axis is to the left of the graph's viewbox/range
|
|
28781
|
-
// otherwise it will overlap with graph axis labels
|
|
28782
|
-
if (label === 0 && !axisOutOfBounds) {
|
|
28783
|
-
showLabel = false;
|
|
28784
|
-
}
|
|
28785
|
-
return showLabel;
|
|
28786
|
-
};
|
|
28787
|
-
|
|
28788
|
-
// Generate the tick labels for an axis based on the tick step
|
|
28789
|
-
function generateTickLocations$1(tickStep, min, max) {
|
|
28790
|
-
const positiveTicks = [];
|
|
28791
|
-
const negativeTicks = [];
|
|
28792
|
-
const ticks = [];
|
|
28793
|
-
|
|
28794
|
-
// Add 0 if it is applicable.
|
|
28795
|
-
// This will be conditionally rendered using showTickLabel.
|
|
28796
|
-
if (min <= 0 && max >= 0) {
|
|
28797
|
-
positiveTicks.push(0);
|
|
28798
|
-
}
|
|
28799
|
-
|
|
28800
|
-
// Add ticks on the positive axis
|
|
28801
|
-
// Start at the first tick after 0 or the minimum value,
|
|
28802
|
-
// taking into account that the graph may be zoomed in on the positive axis
|
|
28803
|
-
let i = Math.max(min, 0) + tickStep;
|
|
28804
|
-
for (i; min < i && i < max; i += tickStep) {
|
|
28805
|
-
positiveTicks.push(i);
|
|
28806
|
-
}
|
|
28807
|
-
|
|
28808
|
-
// Add ticks on the negative axis
|
|
28809
|
-
// Start at the first tick before 0 or the maximum value,
|
|
28810
|
-
// taking into account that the graph may be zoomed in on the negative axis
|
|
28811
|
-
i = Math.min(max, 0) - tickStep;
|
|
28812
|
-
for (i; i > min; i -= tickStep) {
|
|
28813
|
-
negativeTicks.push(i);
|
|
28814
|
-
}
|
|
28815
|
-
|
|
28816
|
-
// Reverse the ticks so that they are in the correct render order
|
|
28817
|
-
positiveTicks.reverse();
|
|
28818
|
-
ticks.push(...positiveTicks, ...negativeTicks);
|
|
28819
|
-
return ticks;
|
|
28820
|
-
}
|
|
28821
|
-
|
|
28822
|
-
function pathBuilder() {
|
|
28823
|
-
return new PathBuilder();
|
|
28824
|
-
}
|
|
28825
|
-
class PathBuilder {
|
|
28826
|
-
constructor() {
|
|
28827
|
-
this.path = [];
|
|
28828
|
-
this.scaleFactor = 1;
|
|
28829
|
-
}
|
|
28830
|
-
build() {
|
|
28831
|
-
return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
|
|
28832
|
-
}
|
|
28833
|
-
|
|
28834
|
-
// add a move (M) command to the path
|
|
28835
|
-
move(x, y) {
|
|
28836
|
-
this.path.push({
|
|
28837
|
-
action: "M",
|
|
28838
|
-
args: [x, y]
|
|
28839
|
-
});
|
|
28840
|
-
return this;
|
|
28841
|
-
}
|
|
28842
|
-
line(x, y) {
|
|
28843
|
-
this.path.push({
|
|
28844
|
-
action: "L",
|
|
28845
|
-
args: [x, y]
|
|
28846
|
-
});
|
|
28847
|
-
return this;
|
|
28848
|
-
}
|
|
28849
|
-
circularArc(radius, toX, toY, {
|
|
28850
|
-
sweep = false,
|
|
28851
|
-
largeArc = false
|
|
28852
|
-
} = {}) {
|
|
28853
|
-
this.path.push({
|
|
28854
|
-
action: "A",
|
|
28855
|
-
args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
|
|
28856
|
-
});
|
|
28857
|
-
return this;
|
|
28858
|
-
}
|
|
28859
|
-
|
|
28860
|
-
// add a curve (C) command to the path
|
|
28861
|
-
curve(control1X, control1Y, control2X, control2Y, endX, endY) {
|
|
28862
|
-
this.path.push({
|
|
28863
|
-
action: "C",
|
|
28864
|
-
args: [control1X, control1Y, control2X, control2Y, endX, endY]
|
|
28865
|
-
});
|
|
28866
|
-
return this;
|
|
28867
|
-
}
|
|
28868
|
-
|
|
28869
|
-
// Scale all coordinates of the path by a factor
|
|
28870
|
-
scale(factor) {
|
|
28871
|
-
this.scaleFactor *= factor;
|
|
28872
|
-
return this;
|
|
28873
|
-
}
|
|
28874
|
-
}
|
|
28875
|
-
function commandToString(command) {
|
|
28876
|
-
return `${command.action}${command.args.join(" ")}`;
|
|
28877
|
-
}
|
|
28878
|
-
function scaleCommandBy(scaleFactor) {
|
|
28879
|
-
return command => {
|
|
28880
|
-
switch (command.action) {
|
|
28881
|
-
case "A":
|
|
28882
|
-
// TODO(benchristel): do we want to refactor this switch to
|
|
28883
|
-
// use polymorphism?
|
|
28884
|
-
// Arc command
|
|
28885
|
-
return _extends({}, command, {
|
|
28886
|
-
args: [command.args[0] * scaleFactor,
|
|
28887
|
-
// x radius
|
|
28888
|
-
command.args[1] * scaleFactor,
|
|
28889
|
-
// y radius
|
|
28890
|
-
command.args[2],
|
|
28891
|
-
// rotation
|
|
28892
|
-
command.args[3],
|
|
28893
|
-
// largeArc flag
|
|
28894
|
-
command.args[4],
|
|
28895
|
-
// sweep flag
|
|
28896
|
-
command.args[5] * scaleFactor,
|
|
28897
|
-
// end x
|
|
28898
|
-
command.args[6] * scaleFactor // end y
|
|
28899
|
-
]
|
|
28900
|
-
});
|
|
28901
|
-
|
|
28902
|
-
default:
|
|
28903
|
-
return _extends({}, command, {
|
|
28904
|
-
args: command.args.map(c => c * scaleFactor)
|
|
28905
|
-
});
|
|
28906
|
-
}
|
|
28907
|
-
};
|
|
28908
|
-
}
|
|
28909
|
-
|
|
28910
28627
|
// We use the pathBuilder here to scale up the SVG path coordinates used
|
|
28911
28628
|
// elsewhere for arrowheads. We scale in the path itself instead of using a CSS
|
|
28912
28629
|
// transform because a transform would also scale up the stroke weight, and we
|
|
@@ -28937,7 +28654,10 @@ function AxisArrows() {
|
|
|
28937
28654
|
range: [[xMin, xMax], [yMin, yMax]]
|
|
28938
28655
|
} = useGraphConfig();
|
|
28939
28656
|
const axisColor = "var(--mafs-fg)";
|
|
28940
|
-
|
|
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, {
|
|
28941
28661
|
color: axisColor,
|
|
28942
28662
|
tip: [xMin, 0],
|
|
28943
28663
|
angle: 180
|
|
@@ -28945,7 +28665,7 @@ function AxisArrows() {
|
|
|
28945
28665
|
color: axisColor,
|
|
28946
28666
|
tip: [xMax, 0],
|
|
28947
28667
|
angle: 0
|
|
28948
|
-
}), /*#__PURE__*/React__default.createElement(Arrowhead, {
|
|
28668
|
+
})), !(xMin > 0 || xMax < 0) && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrowhead, {
|
|
28949
28669
|
color: axisColor,
|
|
28950
28670
|
tip: [0, yMin],
|
|
28951
28671
|
angle: 90
|
|
@@ -28953,38 +28673,75 @@ function AxisArrows() {
|
|
|
28953
28673
|
color: axisColor,
|
|
28954
28674
|
tip: [0, yMax],
|
|
28955
28675
|
angle: 270
|
|
28956
|
-
}));
|
|
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)));
|
|
28957
28719
|
}
|
|
28958
28720
|
|
|
28721
|
+
// The size of the ticks and labels in pixels
|
|
28959
28722
|
const tickSize = 10;
|
|
28960
|
-
const
|
|
28961
|
-
stroke: "black",
|
|
28962
|
-
strokeWidth: 1
|
|
28963
|
-
};
|
|
28723
|
+
const tickLabelSize = 14;
|
|
28964
28724
|
const YGridTick = ({
|
|
28965
28725
|
y,
|
|
28966
|
-
|
|
28726
|
+
range
|
|
28967
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.
|
|
28968
28731
|
let xPointOnAxis = 0;
|
|
28969
|
-
|
|
28970
|
-
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
28971
|
-
// even if they are outside the graph's range.
|
|
28972
|
-
if (graphInfo.range[X][MIN] > 0) {
|
|
28732
|
+
if (range[X][MIN] > 0) {
|
|
28973
28733
|
// If the graph is on the positive side of the x-axis, lock the ticks to the left side of the graph
|
|
28974
|
-
xPointOnAxis =
|
|
28734
|
+
xPointOnAxis = range[X][MIN];
|
|
28975
28735
|
}
|
|
28976
|
-
if (
|
|
28736
|
+
if (range[X][MAX] < 0) {
|
|
28977
28737
|
// If the graph is on the negative side of the x-axis, lock the ticks to the right side of the graph
|
|
28978
|
-
xPointOnAxis =
|
|
28738
|
+
xPointOnAxis = range[X][MAX];
|
|
28979
28739
|
}
|
|
28740
|
+
|
|
28741
|
+
// Convert the Vector2 coordinates to pixel coordinates
|
|
28980
28742
|
const pointOnAxis = [xPointOnAxis, y];
|
|
28981
28743
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
28982
28744
|
|
|
28983
|
-
// If the tick is on the edge of the graph's range, don't render it
|
|
28984
|
-
if (yPosition === -graphInfo.height || yPosition === graphInfo.height + 20) {
|
|
28985
|
-
return null;
|
|
28986
|
-
}
|
|
28987
|
-
|
|
28988
28745
|
// Position of the start of the tick
|
|
28989
28746
|
const x1 = xPosition - tickSize / 2;
|
|
28990
28747
|
const y1 = yPosition;
|
|
@@ -28992,39 +28749,55 @@ const YGridTick = ({
|
|
|
28992
28749
|
// Position of the end of the tick
|
|
28993
28750
|
const x2 = xPosition + tickSize / 2;
|
|
28994
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);
|
|
28995
28762
|
return /*#__PURE__*/React.createElement("g", {
|
|
28996
|
-
className: "
|
|
28763
|
+
className: "tick"
|
|
28997
28764
|
}, /*#__PURE__*/React.createElement("line", {
|
|
28998
28765
|
x1: x1,
|
|
28999
28766
|
y1: y1,
|
|
29000
28767
|
x2: x2,
|
|
29001
28768
|
y2: y2,
|
|
29002
|
-
|
|
29003
|
-
})
|
|
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()));
|
|
29004
28779
|
};
|
|
29005
28780
|
const XGridTick = ({
|
|
29006
28781
|
x,
|
|
29007
|
-
|
|
28782
|
+
range
|
|
29008
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.
|
|
29009
28787
|
let yPointOnAxis = 0;
|
|
29010
|
-
|
|
29011
|
-
// even if they are outside the graph's range.
|
|
29012
|
-
if (graphInfo.range[Y][MIN] > 0) {
|
|
28788
|
+
if (range[Y][MIN] > 0) {
|
|
29013
28789
|
// If the graph is on the positive side of the y-axis, lock the ticks to the top of the graph
|
|
29014
|
-
yPointOnAxis =
|
|
28790
|
+
yPointOnAxis = range[Y][MIN];
|
|
29015
28791
|
}
|
|
29016
|
-
if (
|
|
28792
|
+
if (range[Y][MAX] < 0) {
|
|
29017
28793
|
// If the graph is on the negative side of the x-axis, lock the ticks to the bottom of the graph
|
|
29018
|
-
yPointOnAxis =
|
|
28794
|
+
yPointOnAxis = range[Y][MAX];
|
|
29019
28795
|
}
|
|
28796
|
+
|
|
28797
|
+
// Convert the Vector2 coordinates to pixel coordinates
|
|
29020
28798
|
const pointOnAxis = [x, yPointOnAxis];
|
|
29021
28799
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
29022
28800
|
|
|
29023
|
-
// If the tick is on the edge of the graph's range, don't render it
|
|
29024
|
-
if (xPosition === -graphInfo.width / 2 || xPosition === graphInfo.width) {
|
|
29025
|
-
return null;
|
|
29026
|
-
}
|
|
29027
|
-
|
|
29028
28801
|
// Position of the start of the tick
|
|
29029
28802
|
const x1 = xPosition;
|
|
29030
28803
|
const y1 = yPosition + tickSize / 2;
|
|
@@ -29032,21 +28805,56 @@ const XGridTick = ({
|
|
|
29032
28805
|
// Position of the end of the tick
|
|
29033
28806
|
const x2 = xPosition;
|
|
29034
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;
|
|
29035
28821
|
return /*#__PURE__*/React.createElement("g", {
|
|
29036
|
-
className: "
|
|
28822
|
+
className: "tick"
|
|
29037
28823
|
}, /*#__PURE__*/React.createElement("line", {
|
|
29038
28824
|
x1: x1,
|
|
29039
28825
|
y1: y1,
|
|
29040
28826
|
x2: x2,
|
|
29041
28827
|
y2: y2,
|
|
29042
|
-
|
|
29043
|
-
})
|
|
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;
|
|
29044
28851
|
};
|
|
29045
28852
|
function generateTickLocations(tickStep, min, max) {
|
|
29046
28853
|
const ticks = [];
|
|
29047
28854
|
|
|
29048
28855
|
// Add ticks in the positive direction
|
|
29049
|
-
|
|
28856
|
+
const start = Math.max(min, 0);
|
|
28857
|
+
for (let _i = start + tickStep; _i < max; _i += tickStep) {
|
|
29050
28858
|
ticks.push(_i);
|
|
29051
28859
|
}
|
|
29052
28860
|
|
|
@@ -29061,34 +28869,34 @@ function generateTickLocations(tickStep, min, max) {
|
|
|
29061
28869
|
const AxisTicks = () => {
|
|
29062
28870
|
const {
|
|
29063
28871
|
tickStep,
|
|
29064
|
-
range
|
|
29065
|
-
width,
|
|
29066
|
-
height
|
|
28872
|
+
range
|
|
29067
28873
|
} = useGraphConfig();
|
|
29068
|
-
const graphInfo = {
|
|
29069
|
-
range,
|
|
29070
|
-
width,
|
|
29071
|
-
height
|
|
29072
|
-
};
|
|
29073
28874
|
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
29074
28875
|
const [xTickStep, yTickStep] = tickStep;
|
|
28876
|
+
|
|
28877
|
+
// Generate the tick locations & labels for the x and y axes
|
|
29075
28878
|
const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
|
|
29076
28879
|
const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
|
|
29077
28880
|
return /*#__PURE__*/React.createElement("g", {
|
|
29078
|
-
className: "axis-ticks"
|
|
28881
|
+
className: "axis-ticks",
|
|
28882
|
+
role: "presentation"
|
|
28883
|
+
}, /*#__PURE__*/React.createElement("g", {
|
|
28884
|
+
className: "y-axis-ticks"
|
|
29079
28885
|
}, yGridTicks.map(y => {
|
|
29080
28886
|
return /*#__PURE__*/React.createElement(YGridTick, {
|
|
29081
28887
|
y: y,
|
|
29082
28888
|
key: `y-grid-tick-${y}`,
|
|
29083
|
-
|
|
28889
|
+
range: range
|
|
29084
28890
|
});
|
|
29085
|
-
}),
|
|
28891
|
+
})), /*#__PURE__*/React.createElement("g", {
|
|
28892
|
+
className: "x-axis-ticks"
|
|
28893
|
+
}, xGridTicks.map(x => {
|
|
29086
28894
|
return /*#__PURE__*/React.createElement(XGridTick, {
|
|
29087
28895
|
x: x,
|
|
29088
28896
|
key: `x-grid-tick-${x}`,
|
|
29089
|
-
|
|
28897
|
+
range: range
|
|
29090
28898
|
});
|
|
29091
|
-
}));
|
|
28899
|
+
})));
|
|
29092
28900
|
};
|
|
29093
28901
|
|
|
29094
28902
|
/**
|
|
@@ -29109,12 +28917,10 @@ const axisOptions = (props, axisIndex) => {
|
|
|
29109
28917
|
};
|
|
29110
28918
|
};
|
|
29111
28919
|
const Grid = props => {
|
|
29112
|
-
return props.markings === "none" ? null : /*#__PURE__*/React.createElement(
|
|
28920
|
+
return props.markings === "none" ? null : /*#__PURE__*/React.createElement(Coordinates.Cartesian, {
|
|
29113
28921
|
xAxis: axisOptions(props, X),
|
|
29114
28922
|
yAxis: axisOptions(props, Y)
|
|
29115
|
-
})
|
|
29116
|
-
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
29117
|
-
props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)));
|
|
28923
|
+
});
|
|
29118
28924
|
};
|
|
29119
28925
|
|
|
29120
28926
|
// Displays a stylized open eye: lint warnings are visible
|
|
@@ -32035,6 +31841,22 @@ const MafsGraph = props => {
|
|
|
32035
31841
|
const uniqueId = React.useId();
|
|
32036
31842
|
const descriptionId = `interactive-graph-description-${uniqueId}`;
|
|
32037
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
|
+
};
|
|
32038
31860
|
return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
|
|
32039
31861
|
value: {
|
|
32040
31862
|
range: state.range,
|
|
@@ -32056,7 +31878,7 @@ const MafsGraph = props => {
|
|
|
32056
31878
|
padding: "25px 25px 0 0",
|
|
32057
31879
|
boxSizing: "content-box",
|
|
32058
31880
|
marginLeft: "20px",
|
|
32059
|
-
marginBottom: "
|
|
31881
|
+
marginBottom: "30px",
|
|
32060
31882
|
pointerEvents: props.static ? "none" : "auto",
|
|
32061
31883
|
userSelect: "none",
|
|
32062
31884
|
width,
|
|
@@ -32092,7 +31914,7 @@ const MafsGraph = props => {
|
|
|
32092
31914
|
bottom: 0,
|
|
32093
31915
|
left: 0
|
|
32094
31916
|
}
|
|
32095
|
-
}, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null)
|
|
31917
|
+
}, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
|
|
32096
31918
|
preserveAspectRatio: false,
|
|
32097
31919
|
viewBox: {
|
|
32098
31920
|
x: state.range[X],
|
|
@@ -32103,19 +31925,22 @@ const MafsGraph = props => {
|
|
|
32103
31925
|
zoom: false,
|
|
32104
31926
|
width: width,
|
|
32105
31927
|
height: height
|
|
32106
|
-
}, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid, {
|
|
32107
|
-
tickStep: props.step,
|
|
31928
|
+
}, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, /*#__PURE__*/React.createElement(Grid, {
|
|
32108
31929
|
gridStep: props.gridStep,
|
|
32109
31930
|
range: state.range,
|
|
32110
31931
|
containerSizeClass: props.containerSizeClass,
|
|
32111
|
-
markings: props.markings
|
|
32112
|
-
|
|
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, {
|
|
32113
31938
|
lockedFigures: props.lockedFigures,
|
|
32114
31939
|
range: state.range
|
|
32115
31940
|
}), props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
|
|
32116
31941
|
state,
|
|
32117
31942
|
dispatch
|
|
32118
|
-
})))), renderGraphControls({
|
|
31943
|
+
}))))), renderGraphControls({
|
|
32119
31944
|
state,
|
|
32120
31945
|
dispatch
|
|
32121
31946
|
})));
|
|
@@ -32151,6 +31976,56 @@ const renderGraphControls = props => {
|
|
|
32151
31976
|
return null;
|
|
32152
31977
|
}
|
|
32153
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
|
+
};
|
|
32154
32029
|
const renderGraph = props => {
|
|
32155
32030
|
const {
|
|
32156
32031
|
state,
|
|
@@ -44741,7 +44616,7 @@ const init = function init(options) {
|
|
|
44741
44616
|
|
|
44742
44617
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44743
44618
|
const libName = "@khanacademy/perseus";
|
|
44744
|
-
const libVersion = "33.
|
|
44619
|
+
const libVersion = "33.1.0";
|
|
44745
44620
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44746
44621
|
|
|
44747
44622
|
const apiVersion = {
|