@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.css +15 -44
- package/dist/es/index.js +365 -566
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +15 -44
- package/dist/index.d.ts +0 -4
- package/dist/index.js +364 -572
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +0 -7
- package/dist/perseus-types.d.ts +11 -9
- 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/interactive-graph-question-builder.d.ts +4 -0
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +7 -0
- package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +2 -0
- package/dist/widgets/label-image/label-image.d.ts +0 -14
- package/package.json +1 -1
- package/dist/widgets/interaction/arrow-picker.d.ts +0 -12
- package/dist/widgets/interaction/color-picker.d.ts +0 -14
- package/dist/widgets/interaction/dash-picker.d.ts +0 -12
- package/dist/widgets/interaction/element-container.d.ts +0 -22
- package/dist/widgets/interactive-graphs/backgrounds/axis-tick-labels.d.ts +0 -13
package/dist/index.js
CHANGED
|
@@ -10147,11 +10147,35 @@ const setLabelMargins = function (span, size) {
|
|
|
10147
10147
|
marginTop: -height / 2 - y * scale
|
|
10148
10148
|
});
|
|
10149
10149
|
} else {
|
|
10150
|
+
const $container = $span.closest(".svg-image");
|
|
10151
|
+
// Ensuring that the line-height of the text doesn't throw off placement of the text element.
|
|
10152
|
+
// Inherited line-height values can really mess up placement.
|
|
10153
|
+
$container.css("line-height", "normal");
|
|
10154
|
+
|
|
10155
|
+
// The 'max-width' value is the same as the expected (100% scale) width of the graphie.
|
|
10156
|
+
// We are using jQuery to get this information since we don't have a way to pass it to this function.
|
|
10157
|
+
const expectedWidth = $container.css("max-width") ?? "0px";
|
|
10158
|
+
// We can calculate the true scale of the graphie by taking actual width and dividing by the expected width.
|
|
10159
|
+
// NOTE: Using 'slice' to remove the "px" unit from the end of the expected width value.
|
|
10160
|
+
const scale = ($container.width() ?? 0) / parseInt(expectedWidth.replace(/px$/, "")) * 100;
|
|
10161
|
+
|
|
10162
|
+
// Any padding needs to be scaled accordingly.
|
|
10163
|
+
const currentPadding = $span.css("padding") ?? "0px";
|
|
10164
|
+
const newPadding = Math.round(parseInt(currentPadding.slice(0, -2)) * scale) / 100;
|
|
10165
|
+
|
|
10166
|
+
// "multipliers" basically move the position of the text by using 1, 0, -1
|
|
10167
|
+
// 'margin-left' and 'margin-top' are used to position the text.
|
|
10168
|
+
// Margin and font size need to be scaled accordingly.
|
|
10150
10169
|
const multipliers = labelDirections[direction || "center"];
|
|
10151
|
-
|
|
10152
|
-
marginLeft: Math.round(width * multipliers[0]),
|
|
10153
|
-
marginTop: Math.round(height * multipliers[1])
|
|
10154
|
-
|
|
10170
|
+
const styling = {
|
|
10171
|
+
marginLeft: Math.round(width * multipliers[0] * scale) / 100,
|
|
10172
|
+
marginTop: Math.round(height * multipliers[1] * scale) / 100,
|
|
10173
|
+
padding: `${newPadding}px`
|
|
10174
|
+
};
|
|
10175
|
+
if (scale !== 1) {
|
|
10176
|
+
styling["fontSize"] = `${Math.round(scale * 100) / 100}%`;
|
|
10177
|
+
}
|
|
10178
|
+
$span.css(styling);
|
|
10155
10179
|
}
|
|
10156
10180
|
};
|
|
10157
10181
|
const GraphUtils = {
|
|
@@ -17536,9 +17560,16 @@ class SvgImage extends React__namespace.Component {
|
|
|
17536
17560
|
// without coordinates. They don't seem to have any content, so
|
|
17537
17561
|
// it seems fine to just ignore them (rather than error), but
|
|
17538
17562
|
// we should figure out why this is happening.
|
|
17539
|
-
|
|
17563
|
+
|
|
17564
|
+
// 'styling' - When a default scale (1) is used,
|
|
17565
|
+
// setting the font size to 100% is redundant.
|
|
17566
|
+
// Also, setting the font size to 100% can counteract other scale-based styling.
|
|
17567
|
+
// Therefore, when the scale is not applicable,
|
|
17568
|
+
// we set the value to 'null' so that no additional styling will be set.
|
|
17569
|
+
const styling = this.props.scale !== 1 ? {
|
|
17540
17570
|
"font-size": 100 * this.props.scale + "%"
|
|
17541
|
-
}
|
|
17571
|
+
} : null;
|
|
17572
|
+
const label = graphie.label(labelData.coordinates, labelData.content, labelData.alignment, labelData.typesetAsMath, styling);
|
|
17542
17573
|
|
|
17543
17574
|
// Convert absolute positioning css from pixels to percentages
|
|
17544
17575
|
// TODO(alex): Dynamically resize font-size as well. This
|
|
@@ -28586,6 +28617,95 @@ class Graph extends React__namespace.Component {
|
|
|
28586
28617
|
}
|
|
28587
28618
|
}
|
|
28588
28619
|
|
|
28620
|
+
function pathBuilder() {
|
|
28621
|
+
return new PathBuilder();
|
|
28622
|
+
}
|
|
28623
|
+
class PathBuilder {
|
|
28624
|
+
path = [];
|
|
28625
|
+
scaleFactor = 1;
|
|
28626
|
+
build() {
|
|
28627
|
+
return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
|
|
28628
|
+
}
|
|
28629
|
+
|
|
28630
|
+
// add a move (M) command to the path
|
|
28631
|
+
move(x, y) {
|
|
28632
|
+
this.path.push({
|
|
28633
|
+
action: "M",
|
|
28634
|
+
args: [x, y]
|
|
28635
|
+
});
|
|
28636
|
+
return this;
|
|
28637
|
+
}
|
|
28638
|
+
line(x, y) {
|
|
28639
|
+
this.path.push({
|
|
28640
|
+
action: "L",
|
|
28641
|
+
args: [x, y]
|
|
28642
|
+
});
|
|
28643
|
+
return this;
|
|
28644
|
+
}
|
|
28645
|
+
circularArc(radius, toX, toY) {
|
|
28646
|
+
let {
|
|
28647
|
+
sweep = false,
|
|
28648
|
+
largeArc = false
|
|
28649
|
+
} = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
28650
|
+
this.path.push({
|
|
28651
|
+
action: "A",
|
|
28652
|
+
args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
|
|
28653
|
+
});
|
|
28654
|
+
return this;
|
|
28655
|
+
}
|
|
28656
|
+
|
|
28657
|
+
// add a curve (C) command to the path
|
|
28658
|
+
curve(control1X, control1Y, control2X, control2Y, endX, endY) {
|
|
28659
|
+
this.path.push({
|
|
28660
|
+
action: "C",
|
|
28661
|
+
args: [control1X, control1Y, control2X, control2Y, endX, endY]
|
|
28662
|
+
});
|
|
28663
|
+
return this;
|
|
28664
|
+
}
|
|
28665
|
+
|
|
28666
|
+
// Scale all coordinates of the path by a factor
|
|
28667
|
+
scale(factor) {
|
|
28668
|
+
this.scaleFactor *= factor;
|
|
28669
|
+
return this;
|
|
28670
|
+
}
|
|
28671
|
+
}
|
|
28672
|
+
function commandToString(command) {
|
|
28673
|
+
return `${command.action}${command.args.join(" ")}`;
|
|
28674
|
+
}
|
|
28675
|
+
function scaleCommandBy(scaleFactor) {
|
|
28676
|
+
return command => {
|
|
28677
|
+
switch (command.action) {
|
|
28678
|
+
case "A":
|
|
28679
|
+
// TODO(benchristel): do we want to refactor this switch to
|
|
28680
|
+
// use polymorphism?
|
|
28681
|
+
// Arc command
|
|
28682
|
+
return {
|
|
28683
|
+
...command,
|
|
28684
|
+
args: [command.args[0] * scaleFactor,
|
|
28685
|
+
// x radius
|
|
28686
|
+
command.args[1] * scaleFactor,
|
|
28687
|
+
// y radius
|
|
28688
|
+
command.args[2],
|
|
28689
|
+
// rotation
|
|
28690
|
+
command.args[3],
|
|
28691
|
+
// largeArc flag
|
|
28692
|
+
command.args[4],
|
|
28693
|
+
// sweep flag
|
|
28694
|
+
command.args[5] * scaleFactor,
|
|
28695
|
+
// end x
|
|
28696
|
+
command.args[6] * scaleFactor // end y
|
|
28697
|
+
]
|
|
28698
|
+
};
|
|
28699
|
+
|
|
28700
|
+
default:
|
|
28701
|
+
return {
|
|
28702
|
+
...command,
|
|
28703
|
+
args: command.args.map(c => c * scaleFactor)
|
|
28704
|
+
};
|
|
28705
|
+
}
|
|
28706
|
+
};
|
|
28707
|
+
}
|
|
28708
|
+
|
|
28589
28709
|
// Restricts `value` to be between `min` and `max`.
|
|
28590
28710
|
function clamp(value, min, max) {
|
|
28591
28711
|
if (value < min) {
|
|
@@ -28815,380 +28935,6 @@ function pixelsToVectors(pixels, graphState) {
|
|
|
28815
28935
|
});
|
|
28816
28936
|
}
|
|
28817
28937
|
|
|
28818
|
-
function AxisLabels() {
|
|
28819
|
-
const {
|
|
28820
|
-
range,
|
|
28821
|
-
labels,
|
|
28822
|
-
width,
|
|
28823
|
-
height
|
|
28824
|
-
} = useGraphConfig();
|
|
28825
|
-
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28826
|
-
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28827
|
-
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
28828
|
-
const graphInfo = {
|
|
28829
|
-
range,
|
|
28830
|
-
width,
|
|
28831
|
-
height
|
|
28832
|
-
};
|
|
28833
|
-
const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
|
|
28834
|
-
// The default location for the y-axis-label is at the maximum y point
|
|
28835
|
-
// This is just underneath the tick line for that point. The -24 moves the
|
|
28836
|
-
// label up one grid square, so it sits on top of the graph
|
|
28837
|
-
const [x2, y2] = mafs.vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
|
|
28838
|
-
const {
|
|
28839
|
-
TeX
|
|
28840
|
-
} = getDependencies();
|
|
28841
|
-
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("span", {
|
|
28842
|
-
style: {
|
|
28843
|
-
position: "absolute",
|
|
28844
|
-
left: x1,
|
|
28845
|
-
top: y1,
|
|
28846
|
-
fontSize: "14px",
|
|
28847
|
-
transform: "translate(7px, -50%)"
|
|
28848
|
-
}
|
|
28849
|
-
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
28850
|
-
style: {
|
|
28851
|
-
position: "absolute",
|
|
28852
|
-
left: x2,
|
|
28853
|
-
top: y2,
|
|
28854
|
-
fontSize: "14px",
|
|
28855
|
-
transform: "translate(-50%, 0px)"
|
|
28856
|
-
}
|
|
28857
|
-
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, yAxisLabelText)));
|
|
28858
|
-
}
|
|
28859
|
-
|
|
28860
|
-
// Universal padding for the axis ticks to ensure that the labels
|
|
28861
|
-
// have enough space to render without overlapping the tick lines
|
|
28862
|
-
const tickPadding = 5;
|
|
28863
|
-
const AxisTickLabels = () => {
|
|
28864
|
-
const graphConfig = useGraphConfig();
|
|
28865
|
-
const {
|
|
28866
|
-
tickStep,
|
|
28867
|
-
range
|
|
28868
|
-
} = graphConfig;
|
|
28869
|
-
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
28870
|
-
const [xTickStep, yTickStep] = tickStep;
|
|
28871
|
-
const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
|
|
28872
|
-
const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
|
|
28873
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
28874
|
-
className: "axis-tick-labels"
|
|
28875
|
-
}, /*#__PURE__*/React__namespace.createElement(YGridAxis, {
|
|
28876
|
-
axisTicks: yGridTicks,
|
|
28877
|
-
graphConfig: graphConfig
|
|
28878
|
-
}), /*#__PURE__*/React__namespace.createElement(XGridAxis, {
|
|
28879
|
-
axisTicks: xGridTicks,
|
|
28880
|
-
graphConfig: graphConfig
|
|
28881
|
-
}));
|
|
28882
|
-
};
|
|
28883
|
-
|
|
28884
|
-
// Generate the necessary styles and ticks for the y-axis tick labels container
|
|
28885
|
-
const YGridAxis = props => {
|
|
28886
|
-
const {
|
|
28887
|
-
axisTicks,
|
|
28888
|
-
graphConfig
|
|
28889
|
-
} = props;
|
|
28890
|
-
const {
|
|
28891
|
-
width,
|
|
28892
|
-
height
|
|
28893
|
-
} = graphConfig;
|
|
28894
|
-
|
|
28895
|
-
// First get the label height by calculating the difference between the first two y-axis labels
|
|
28896
|
-
const yTopPoint = pointToPixel([0, axisTicks[0]], graphConfig);
|
|
28897
|
-
const ySecondPoint = pointToPixel([0, axisTicks[1]], graphConfig);
|
|
28898
|
-
const yLabelHeight = ySecondPoint[1] - yTopPoint[1];
|
|
28899
|
-
|
|
28900
|
-
// When rendering purely positive y-axis labels, we want to adjust the left position
|
|
28901
|
-
// from the lowest calculated tick, as yMin may be negative
|
|
28902
|
-
const yLowestTick = axisTicks[axisTicks.length - 1];
|
|
28903
|
-
const yLeftAdjustment = Math.min(Math.max(yTopPoint[0] - tickPadding, 0), width);
|
|
28904
|
-
|
|
28905
|
-
// We only want to render the 0 label on the y-axis if it is left of the grid
|
|
28906
|
-
const yAxisIsLeftOfGrid = yLeftAdjustment === 0;
|
|
28907
|
-
|
|
28908
|
-
// If the y-axis is right of the graph's viewbox/range,
|
|
28909
|
-
// we want to adjust both the positioning and text alignment
|
|
28910
|
-
const yAxisRightOfGrid = yLeftAdjustment === width;
|
|
28911
|
-
|
|
28912
|
-
// Determine the default spacing for the labels based on whether the y-axis is right of the grid
|
|
28913
|
-
const defaultSpacing = yAxisRightOfGrid ? 0.5 : 0;
|
|
28914
|
-
|
|
28915
|
-
// We also want to adjust the spacing if there are no negative y-axis labels as the extra space is not needed
|
|
28916
|
-
const marginSpacing = yLowestTick >= 0 ? defaultSpacing * 2 : defaultSpacing;
|
|
28917
|
-
|
|
28918
|
-
// Determine the text alignment based on whether the y-axis is right of the grid
|
|
28919
|
-
// We need to cast this as a const as it is used in the inline styles
|
|
28920
|
-
const textAlign = yAxisRightOfGrid ? "left" : "right";
|
|
28921
|
-
const yAxisStyles = {
|
|
28922
|
-
left: `calc(${yLeftAdjustment}px - ${marginSpacing}em)`,
|
|
28923
|
-
top: `calc(${yTopPoint[1]}px - 0.5em)`,
|
|
28924
|
-
height,
|
|
28925
|
-
textAlign,
|
|
28926
|
-
"--y-axis-label-height": yLabelHeight + "px"
|
|
28927
|
-
};
|
|
28928
|
-
const classNames = yAxisRightOfGrid ? "y-axis-tick-labels y-axis-right-of-grid" : "y-axis-tick-labels ";
|
|
28929
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
28930
|
-
className: classNames,
|
|
28931
|
-
"data-testid": "y-axis-tick-labels",
|
|
28932
|
-
style: yAxisStyles
|
|
28933
|
-
}, axisTicks.map(label => {
|
|
28934
|
-
return /*#__PURE__*/React__namespace.createElement(AxisTickLabel, {
|
|
28935
|
-
label: label,
|
|
28936
|
-
axis: "y",
|
|
28937
|
-
key: `y-grid-tick-${label}`,
|
|
28938
|
-
graphConfig: graphConfig,
|
|
28939
|
-
axisOutOfBounds: yAxisIsLeftOfGrid
|
|
28940
|
-
});
|
|
28941
|
-
}));
|
|
28942
|
-
};
|
|
28943
|
-
|
|
28944
|
-
// Generate the necessary styles and ticks for the x-axis tick labels container
|
|
28945
|
-
const XGridAxis = props => {
|
|
28946
|
-
const {
|
|
28947
|
-
axisTicks,
|
|
28948
|
-
graphConfig
|
|
28949
|
-
} = props;
|
|
28950
|
-
const {
|
|
28951
|
-
width,
|
|
28952
|
-
height
|
|
28953
|
-
} = graphConfig;
|
|
28954
|
-
|
|
28955
|
-
// Determine label width by calculating the difference between the first two x-axis labels
|
|
28956
|
-
const xFirstPoint = pointToPixel([axisTicks[0], 0], graphConfig);
|
|
28957
|
-
const xSecondPoint = pointToPixel([axisTicks[1], 0], graphConfig);
|
|
28958
|
-
const xLabelWidth = Math.abs(xSecondPoint[0] - xFirstPoint[0]);
|
|
28959
|
-
|
|
28960
|
-
// Determine the top position of the x-axis labels based on
|
|
28961
|
-
// whether the x-axis is above, within, or below the graph
|
|
28962
|
-
const xAxisAboveGraph = xFirstPoint[1] < 0;
|
|
28963
|
-
const xTopAdjustment = xAxisAboveGraph ? 0 : Math.min(xFirstPoint[1] + tickPadding, height);
|
|
28964
|
-
|
|
28965
|
-
// Determine whether the x-axis is outside of the graph's viewbox/range
|
|
28966
|
-
// This is used to conditionally render the 0 label on the x-axis
|
|
28967
|
-
const xAxisOutOfBounds = xTopAdjustment === height || xAxisAboveGraph;
|
|
28968
|
-
|
|
28969
|
-
// Determine the available space to the right of the x-axis labels
|
|
28970
|
-
const availableSpaceRight = width - xFirstPoint[0];
|
|
28971
|
-
|
|
28972
|
-
// Calculate the total combined width of ALL the x-axis labels
|
|
28973
|
-
const axisTicksWidth = axisTicks.length * xLabelWidth;
|
|
28974
|
-
|
|
28975
|
-
// Determine if we need to increase the width of the axis past the graph width
|
|
28976
|
-
const calculatedWidth = axisTicksWidth > width ? axisTicksWidth : width;
|
|
28977
|
-
|
|
28978
|
-
// Determine the right adjustment for the x-axis labels based on the available space
|
|
28979
|
-
// We're adjusting the right as the flex-direction is row-reverse
|
|
28980
|
-
const xRightAdjustment = availableSpaceRight - xLabelWidth / 2;
|
|
28981
|
-
const xAxisStyles = {
|
|
28982
|
-
top: xTopAdjustment,
|
|
28983
|
-
right: xRightAdjustment,
|
|
28984
|
-
width: calculatedWidth,
|
|
28985
|
-
"--x-axis-label-width": xLabelWidth + "px"
|
|
28986
|
-
};
|
|
28987
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
28988
|
-
className: `x-axis-tick-labels ${xAxisAboveGraph && "x-axis-top-of-grid"}`,
|
|
28989
|
-
"data-testid": "x-axis-tick-labels",
|
|
28990
|
-
style: xAxisStyles
|
|
28991
|
-
}, axisTicks.map(label => {
|
|
28992
|
-
return /*#__PURE__*/React__namespace.createElement(AxisTickLabel, {
|
|
28993
|
-
axis: "x",
|
|
28994
|
-
label: label,
|
|
28995
|
-
key: `x-grid-tick-${label}`,
|
|
28996
|
-
graphConfig: graphConfig,
|
|
28997
|
-
axisOutOfBounds: xAxisOutOfBounds
|
|
28998
|
-
});
|
|
28999
|
-
}));
|
|
29000
|
-
};
|
|
29001
|
-
|
|
29002
|
-
// Generate the individual tick label elements
|
|
29003
|
-
const AxisTickLabel = _ref => {
|
|
29004
|
-
let {
|
|
29005
|
-
label,
|
|
29006
|
-
graphConfig,
|
|
29007
|
-
axisOutOfBounds,
|
|
29008
|
-
axis
|
|
29009
|
-
} = _ref;
|
|
29010
|
-
const {
|
|
29011
|
-
gridStep,
|
|
29012
|
-
tickStep
|
|
29013
|
-
} = graphConfig;
|
|
29014
|
-
// Determine the point on the axis based on the axis type
|
|
29015
|
-
const pointOnAxis = axis === "x" ? [label, 0] : [0, label];
|
|
29016
|
-
const pixelPoint = pointToPixel(pointOnAxis, graphConfig);
|
|
29017
|
-
|
|
29018
|
-
// Determine the relevant edge of the graph based on the axis type
|
|
29019
|
-
const graphEdge = axis === "x" ? graphConfig.width : graphConfig.height;
|
|
29020
|
-
|
|
29021
|
-
// Determine the vector index based on the axis type
|
|
29022
|
-
const vectorIndex = axis === "x" ? 0 : 1;
|
|
29023
|
-
|
|
29024
|
-
// Determine whether or not to render the tick label text
|
|
29025
|
-
const shouldShowLabel = showTickLabel({
|
|
29026
|
-
label,
|
|
29027
|
-
axisOutOfBounds,
|
|
29028
|
-
graphEdge,
|
|
29029
|
-
position: pixelPoint[vectorIndex],
|
|
29030
|
-
gridStep: gridStep[vectorIndex],
|
|
29031
|
-
tickStep: tickStep[vectorIndex]
|
|
29032
|
-
});
|
|
29033
|
-
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
29034
|
-
className: `${axis}-axis-tick-label`
|
|
29035
|
-
}, shouldShowLabel && label);
|
|
29036
|
-
};
|
|
29037
|
-
|
|
29038
|
-
// Determine whether or not to show the tick label text
|
|
29039
|
-
const showTickLabel = _ref2 => {
|
|
29040
|
-
let {
|
|
29041
|
-
gridStep,
|
|
29042
|
-
tickStep,
|
|
29043
|
-
label,
|
|
29044
|
-
graphEdge,
|
|
29045
|
-
position,
|
|
29046
|
-
axisOutOfBounds
|
|
29047
|
-
} = _ref2;
|
|
29048
|
-
let showLabel = true;
|
|
29049
|
-
|
|
29050
|
-
// We only want to show the initial negative tick labels (e.g. -1) on each
|
|
29051
|
-
// axis if the tickStep > gridStep, to ensure that these labels do not overlap.
|
|
29052
|
-
// e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
|
|
29053
|
-
// which allows enough room for both these tick labels to render.
|
|
29054
|
-
showLabel = tickStep > gridStep ? true : label !== -tickStep;
|
|
29055
|
-
|
|
29056
|
-
// If the tick label position is greater than the relevant edge of the graph, don't render it
|
|
29057
|
-
if (position <= -graphEdge || position >= graphEdge) {
|
|
29058
|
-
showLabel = false;
|
|
29059
|
-
}
|
|
29060
|
-
|
|
29061
|
-
// If the label is 0, we only want to show it if the axis is to the left of the graph's viewbox/range
|
|
29062
|
-
// otherwise it will overlap with graph axis labels
|
|
29063
|
-
if (label === 0 && !axisOutOfBounds) {
|
|
29064
|
-
showLabel = false;
|
|
29065
|
-
}
|
|
29066
|
-
return showLabel;
|
|
29067
|
-
};
|
|
29068
|
-
|
|
29069
|
-
// Generate the tick labels for an axis based on the tick step
|
|
29070
|
-
function generateTickLocations$1(tickStep, min, max) {
|
|
29071
|
-
const positiveTicks = [];
|
|
29072
|
-
const negativeTicks = [];
|
|
29073
|
-
const ticks = [];
|
|
29074
|
-
|
|
29075
|
-
// Add 0 if it is applicable.
|
|
29076
|
-
// This will be conditionally rendered using showTickLabel.
|
|
29077
|
-
if (min <= 0 && max >= 0) {
|
|
29078
|
-
positiveTicks.push(0);
|
|
29079
|
-
}
|
|
29080
|
-
|
|
29081
|
-
// Add ticks on the positive axis
|
|
29082
|
-
// Start at the first tick after 0 or the minimum value,
|
|
29083
|
-
// taking into account that the graph may be zoomed in on the positive axis
|
|
29084
|
-
let i = Math.max(min, 0) + tickStep;
|
|
29085
|
-
for (i; min < i && i < max; i += tickStep) {
|
|
29086
|
-
positiveTicks.push(i);
|
|
29087
|
-
}
|
|
29088
|
-
|
|
29089
|
-
// Add ticks on the negative axis
|
|
29090
|
-
// Start at the first tick before 0 or the maximum value,
|
|
29091
|
-
// taking into account that the graph may be zoomed in on the negative axis
|
|
29092
|
-
i = Math.min(max, 0) - tickStep;
|
|
29093
|
-
for (i; i > min; i -= tickStep) {
|
|
29094
|
-
negativeTicks.push(i);
|
|
29095
|
-
}
|
|
29096
|
-
|
|
29097
|
-
// Reverse the ticks so that they are in the correct render order
|
|
29098
|
-
positiveTicks.reverse();
|
|
29099
|
-
ticks.push(...positiveTicks, ...negativeTicks);
|
|
29100
|
-
return ticks;
|
|
29101
|
-
}
|
|
29102
|
-
|
|
29103
|
-
function pathBuilder() {
|
|
29104
|
-
return new PathBuilder();
|
|
29105
|
-
}
|
|
29106
|
-
class PathBuilder {
|
|
29107
|
-
path = [];
|
|
29108
|
-
scaleFactor = 1;
|
|
29109
|
-
build() {
|
|
29110
|
-
return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
|
|
29111
|
-
}
|
|
29112
|
-
|
|
29113
|
-
// add a move (M) command to the path
|
|
29114
|
-
move(x, y) {
|
|
29115
|
-
this.path.push({
|
|
29116
|
-
action: "M",
|
|
29117
|
-
args: [x, y]
|
|
29118
|
-
});
|
|
29119
|
-
return this;
|
|
29120
|
-
}
|
|
29121
|
-
line(x, y) {
|
|
29122
|
-
this.path.push({
|
|
29123
|
-
action: "L",
|
|
29124
|
-
args: [x, y]
|
|
29125
|
-
});
|
|
29126
|
-
return this;
|
|
29127
|
-
}
|
|
29128
|
-
circularArc(radius, toX, toY) {
|
|
29129
|
-
let {
|
|
29130
|
-
sweep = false,
|
|
29131
|
-
largeArc = false
|
|
29132
|
-
} = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
29133
|
-
this.path.push({
|
|
29134
|
-
action: "A",
|
|
29135
|
-
args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
|
|
29136
|
-
});
|
|
29137
|
-
return this;
|
|
29138
|
-
}
|
|
29139
|
-
|
|
29140
|
-
// add a curve (C) command to the path
|
|
29141
|
-
curve(control1X, control1Y, control2X, control2Y, endX, endY) {
|
|
29142
|
-
this.path.push({
|
|
29143
|
-
action: "C",
|
|
29144
|
-
args: [control1X, control1Y, control2X, control2Y, endX, endY]
|
|
29145
|
-
});
|
|
29146
|
-
return this;
|
|
29147
|
-
}
|
|
29148
|
-
|
|
29149
|
-
// Scale all coordinates of the path by a factor
|
|
29150
|
-
scale(factor) {
|
|
29151
|
-
this.scaleFactor *= factor;
|
|
29152
|
-
return this;
|
|
29153
|
-
}
|
|
29154
|
-
}
|
|
29155
|
-
function commandToString(command) {
|
|
29156
|
-
return `${command.action}${command.args.join(" ")}`;
|
|
29157
|
-
}
|
|
29158
|
-
function scaleCommandBy(scaleFactor) {
|
|
29159
|
-
return command => {
|
|
29160
|
-
switch (command.action) {
|
|
29161
|
-
case "A":
|
|
29162
|
-
// TODO(benchristel): do we want to refactor this switch to
|
|
29163
|
-
// use polymorphism?
|
|
29164
|
-
// Arc command
|
|
29165
|
-
return {
|
|
29166
|
-
...command,
|
|
29167
|
-
args: [command.args[0] * scaleFactor,
|
|
29168
|
-
// x radius
|
|
29169
|
-
command.args[1] * scaleFactor,
|
|
29170
|
-
// y radius
|
|
29171
|
-
command.args[2],
|
|
29172
|
-
// rotation
|
|
29173
|
-
command.args[3],
|
|
29174
|
-
// largeArc flag
|
|
29175
|
-
command.args[4],
|
|
29176
|
-
// sweep flag
|
|
29177
|
-
command.args[5] * scaleFactor,
|
|
29178
|
-
// end x
|
|
29179
|
-
command.args[6] * scaleFactor // end y
|
|
29180
|
-
]
|
|
29181
|
-
};
|
|
29182
|
-
|
|
29183
|
-
default:
|
|
29184
|
-
return {
|
|
29185
|
-
...command,
|
|
29186
|
-
args: command.args.map(c => c * scaleFactor)
|
|
29187
|
-
};
|
|
29188
|
-
}
|
|
29189
|
-
};
|
|
29190
|
-
}
|
|
29191
|
-
|
|
29192
28938
|
// We use the pathBuilder here to scale up the SVG path coordinates used
|
|
29193
28939
|
// elsewhere for arrowheads. We scale in the path itself instead of using a CSS
|
|
29194
28940
|
// transform because a transform would also scale up the stroke weight, and we
|
|
@@ -29218,7 +28964,10 @@ function AxisArrows() {
|
|
|
29218
28964
|
range: [[xMin, xMax], [yMin, yMax]]
|
|
29219
28965
|
} = useGraphConfig();
|
|
29220
28966
|
const axisColor = "var(--mafs-fg)";
|
|
29221
|
-
|
|
28967
|
+
|
|
28968
|
+
// Only render the arrows if the axis is within the visible range
|
|
28969
|
+
// as otherwise the arrows will be rendered outside the graph
|
|
28970
|
+
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, !(yMin > 0 || yMax < 0) && /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
|
|
29222
28971
|
color: axisColor,
|
|
29223
28972
|
tip: [xMin, 0],
|
|
29224
28973
|
angle: 180
|
|
@@ -29226,7 +28975,7 @@ function AxisArrows() {
|
|
|
29226
28975
|
color: axisColor,
|
|
29227
28976
|
tip: [xMax, 0],
|
|
29228
28977
|
angle: 0
|
|
29229
|
-
}), /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
|
|
28978
|
+
})), !(xMin > 0 || xMax < 0) && /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
|
|
29230
28979
|
color: axisColor,
|
|
29231
28980
|
tip: [0, yMin],
|
|
29232
28981
|
angle: 90
|
|
@@ -29234,39 +28983,76 @@ function AxisArrows() {
|
|
|
29234
28983
|
color: axisColor,
|
|
29235
28984
|
tip: [0, yMax],
|
|
29236
28985
|
angle: 270
|
|
29237
|
-
}));
|
|
28986
|
+
})));
|
|
29238
28987
|
}
|
|
29239
28988
|
|
|
28989
|
+
function AxisLabels() {
|
|
28990
|
+
const {
|
|
28991
|
+
range,
|
|
28992
|
+
labels,
|
|
28993
|
+
width,
|
|
28994
|
+
height
|
|
28995
|
+
} = useGraphConfig();
|
|
28996
|
+
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28997
|
+
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28998
|
+
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
28999
|
+
const graphInfo = {
|
|
29000
|
+
range,
|
|
29001
|
+
width,
|
|
29002
|
+
height
|
|
29003
|
+
};
|
|
29004
|
+
const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
|
|
29005
|
+
// The default location for the y-axis-label is at the maximum y point
|
|
29006
|
+
// This is just underneath the tick line for that point. The -24 moves the
|
|
29007
|
+
// label up one grid square, so it sits on top of the graph
|
|
29008
|
+
const [x2, y2] = mafs.vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
|
|
29009
|
+
const {
|
|
29010
|
+
TeX
|
|
29011
|
+
} = getDependencies();
|
|
29012
|
+
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("span", {
|
|
29013
|
+
style: {
|
|
29014
|
+
position: "absolute",
|
|
29015
|
+
left: x1,
|
|
29016
|
+
top: y1,
|
|
29017
|
+
fontSize: "14px",
|
|
29018
|
+
transform: "translate(7px, -50%)"
|
|
29019
|
+
}
|
|
29020
|
+
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
29021
|
+
style: {
|
|
29022
|
+
position: "absolute",
|
|
29023
|
+
left: x2,
|
|
29024
|
+
top: y2,
|
|
29025
|
+
fontSize: "14px",
|
|
29026
|
+
transform: "translate(-50%, 0px)"
|
|
29027
|
+
}
|
|
29028
|
+
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, yAxisLabelText)));
|
|
29029
|
+
}
|
|
29030
|
+
|
|
29031
|
+
// The size of the ticks and labels in pixels
|
|
29240
29032
|
const tickSize = 10;
|
|
29241
|
-
const
|
|
29242
|
-
stroke: "black",
|
|
29243
|
-
strokeWidth: 1
|
|
29244
|
-
};
|
|
29033
|
+
const tickLabelSize = 14;
|
|
29245
29034
|
const YGridTick = _ref => {
|
|
29246
29035
|
let {
|
|
29247
29036
|
y,
|
|
29248
|
-
|
|
29037
|
+
range
|
|
29249
29038
|
} = _ref;
|
|
29039
|
+
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29040
|
+
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
29041
|
+
// the ticks and labels would render outside of the clipping-mask.
|
|
29250
29042
|
let xPointOnAxis = 0;
|
|
29251
|
-
|
|
29252
|
-
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
29253
|
-
// even if they are outside the graph's range.
|
|
29254
|
-
if (graphInfo.range[X][MIN] > 0) {
|
|
29043
|
+
if (range[X][MIN] > 0) {
|
|
29255
29044
|
// If the graph is on the positive side of the x-axis, lock the ticks to the left side of the graph
|
|
29256
|
-
xPointOnAxis =
|
|
29045
|
+
xPointOnAxis = range[X][MIN];
|
|
29257
29046
|
}
|
|
29258
|
-
if (
|
|
29047
|
+
if (range[X][MAX] < 0) {
|
|
29259
29048
|
// If the graph is on the negative side of the x-axis, lock the ticks to the right side of the graph
|
|
29260
|
-
xPointOnAxis =
|
|
29049
|
+
xPointOnAxis = range[X][MAX];
|
|
29261
29050
|
}
|
|
29051
|
+
|
|
29052
|
+
// Convert the Vector2 coordinates to pixel coordinates
|
|
29262
29053
|
const pointOnAxis = [xPointOnAxis, y];
|
|
29263
29054
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
29264
29055
|
|
|
29265
|
-
// If the tick is on the edge of the graph's range, don't render it
|
|
29266
|
-
if (yPosition === -graphInfo.height || yPosition === graphInfo.height + 20) {
|
|
29267
|
-
return null;
|
|
29268
|
-
}
|
|
29269
|
-
|
|
29270
29056
|
// Position of the start of the tick
|
|
29271
29057
|
const x1 = xPosition - tickSize / 2;
|
|
29272
29058
|
const y1 = yPosition;
|
|
@@ -29274,40 +29060,56 @@ const YGridTick = _ref => {
|
|
|
29274
29060
|
// Position of the end of the tick
|
|
29275
29061
|
const x2 = xPosition + tickSize / 2;
|
|
29276
29062
|
const y2 = yPosition;
|
|
29063
|
+
|
|
29064
|
+
// Adjust the y position of the x-axis labels based on
|
|
29065
|
+
// whether the x-axis is above, within, or below the graph
|
|
29066
|
+
const xAdjustment = range[X][MAX] <= 0 ? tickLabelSize * 1.5 : -tickLabelSize * 1.1;
|
|
29067
|
+
const xPositionText = xPosition + xAdjustment;
|
|
29068
|
+
const yPositionText = yPosition + tickLabelSize * 0.25; // Center the text vertically on the tick
|
|
29069
|
+
|
|
29070
|
+
// If the graph displays both the y and x axis lines within the graph, we want
|
|
29071
|
+
// to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
|
|
29072
|
+
const showLabel = shouldShowLabel(y, range);
|
|
29277
29073
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29278
|
-
className: "
|
|
29074
|
+
className: "tick"
|
|
29279
29075
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
29280
29076
|
x1: x1,
|
|
29281
29077
|
y1: y1,
|
|
29282
29078
|
x2: x2,
|
|
29283
29079
|
y2: y2,
|
|
29284
|
-
|
|
29285
|
-
})
|
|
29080
|
+
className: "axis-tick"
|
|
29081
|
+
}), showLabel && /*#__PURE__*/React__namespace.createElement("text", {
|
|
29082
|
+
className: "axis-tick-label",
|
|
29083
|
+
style: {
|
|
29084
|
+
fontSize: tickLabelSize
|
|
29085
|
+
},
|
|
29086
|
+
textAnchor: "end",
|
|
29087
|
+
x: xPositionText,
|
|
29088
|
+
y: yPositionText
|
|
29089
|
+
}, y.toString()));
|
|
29286
29090
|
};
|
|
29287
29091
|
const XGridTick = _ref2 => {
|
|
29288
29092
|
let {
|
|
29289
29093
|
x,
|
|
29290
|
-
|
|
29094
|
+
range
|
|
29291
29095
|
} = _ref2;
|
|
29096
|
+
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29097
|
+
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
29098
|
+
// the ticks and labels would render outside of the clipping-mask.
|
|
29292
29099
|
let yPointOnAxis = 0;
|
|
29293
|
-
|
|
29294
|
-
// even if they are outside the graph's range.
|
|
29295
|
-
if (graphInfo.range[Y][MIN] > 0) {
|
|
29100
|
+
if (range[Y][MIN] > 0) {
|
|
29296
29101
|
// If the graph is on the positive side of the y-axis, lock the ticks to the top of the graph
|
|
29297
|
-
yPointOnAxis =
|
|
29102
|
+
yPointOnAxis = range[Y][MIN];
|
|
29298
29103
|
}
|
|
29299
|
-
if (
|
|
29104
|
+
if (range[Y][MAX] < 0) {
|
|
29300
29105
|
// If the graph is on the negative side of the x-axis, lock the ticks to the bottom of the graph
|
|
29301
|
-
yPointOnAxis =
|
|
29106
|
+
yPointOnAxis = range[Y][MAX];
|
|
29302
29107
|
}
|
|
29108
|
+
|
|
29109
|
+
// Convert the Vector2 coordinates to pixel coordinates
|
|
29303
29110
|
const pointOnAxis = [x, yPointOnAxis];
|
|
29304
29111
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
29305
29112
|
|
|
29306
|
-
// If the tick is on the edge of the graph's range, don't render it
|
|
29307
|
-
if (xPosition === -graphInfo.width / 2 || xPosition === graphInfo.width) {
|
|
29308
|
-
return null;
|
|
29309
|
-
}
|
|
29310
|
-
|
|
29311
29113
|
// Position of the start of the tick
|
|
29312
29114
|
const x1 = xPosition;
|
|
29313
29115
|
const y1 = yPosition + tickSize / 2;
|
|
@@ -29315,21 +29117,56 @@ const XGridTick = _ref2 => {
|
|
|
29315
29117
|
// Position of the end of the tick
|
|
29316
29118
|
const x2 = xPosition;
|
|
29317
29119
|
const y2 = yPosition - tickSize / 2;
|
|
29120
|
+
|
|
29121
|
+
// Adjust the Y position of the x-axis labels based on
|
|
29122
|
+
// whether the x-axis is above, within, or below the graph
|
|
29123
|
+
const yAdjustment = range[Y][MAX] < 0 ? -tickLabelSize : tickLabelSize * 1.75;
|
|
29124
|
+
|
|
29125
|
+
// Adjust the X position of the x-axis labels based on
|
|
29126
|
+
// whether the label is positive or negative, in order to
|
|
29127
|
+
// account for the width of the negative sign
|
|
29128
|
+
const xAdjustment = x < 0 ? -2 : 0;
|
|
29129
|
+
|
|
29130
|
+
// Apply the adjustments to the x and y positions for the text
|
|
29131
|
+
const xPositionText = xPosition + xAdjustment;
|
|
29132
|
+
const yPositionText = yPosition + yAdjustment;
|
|
29318
29133
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29319
|
-
className: "
|
|
29134
|
+
className: "tick"
|
|
29320
29135
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
29321
29136
|
x1: x1,
|
|
29322
29137
|
y1: y1,
|
|
29323
29138
|
x2: x2,
|
|
29324
29139
|
y2: y2,
|
|
29325
|
-
|
|
29326
|
-
})
|
|
29140
|
+
className: "axis-tick"
|
|
29141
|
+
}), /*#__PURE__*/React__namespace.createElement("text", {
|
|
29142
|
+
className: "axis-tick-label",
|
|
29143
|
+
style: {
|
|
29144
|
+
fontSize: tickLabelSize
|
|
29145
|
+
},
|
|
29146
|
+
textAnchor: "middle",
|
|
29147
|
+
x: xPositionText,
|
|
29148
|
+
y: yPositionText
|
|
29149
|
+
}, x.toString()));
|
|
29150
|
+
};
|
|
29151
|
+
|
|
29152
|
+
// Determines whether to show the label for the given tick
|
|
29153
|
+
// Currently, the only condition is to hide the label at -1
|
|
29154
|
+
// on the y-axis when the x-axis is within the graph
|
|
29155
|
+
const shouldShowLabel = (number, range) => {
|
|
29156
|
+
let showLabel = true;
|
|
29157
|
+
|
|
29158
|
+
// If the x-axis is within the graph and the y-axis is at -1, hide the label
|
|
29159
|
+
if (range[X][MIN] < -1 && range[X][MAX] > 0 && number === -1) {
|
|
29160
|
+
showLabel = false;
|
|
29161
|
+
}
|
|
29162
|
+
return showLabel;
|
|
29327
29163
|
};
|
|
29328
29164
|
function generateTickLocations(tickStep, min, max) {
|
|
29329
29165
|
const ticks = [];
|
|
29330
29166
|
|
|
29331
29167
|
// Add ticks in the positive direction
|
|
29332
|
-
|
|
29168
|
+
const start = Math.max(min, 0);
|
|
29169
|
+
for (let i = start + tickStep; i < max; i += tickStep) {
|
|
29333
29170
|
ticks.push(i);
|
|
29334
29171
|
}
|
|
29335
29172
|
|
|
@@ -29344,34 +29181,34 @@ function generateTickLocations(tickStep, min, max) {
|
|
|
29344
29181
|
const AxisTicks = () => {
|
|
29345
29182
|
const {
|
|
29346
29183
|
tickStep,
|
|
29347
|
-
range
|
|
29348
|
-
width,
|
|
29349
|
-
height
|
|
29184
|
+
range
|
|
29350
29185
|
} = useGraphConfig();
|
|
29351
|
-
const graphInfo = {
|
|
29352
|
-
range,
|
|
29353
|
-
width,
|
|
29354
|
-
height
|
|
29355
|
-
};
|
|
29356
29186
|
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
29357
29187
|
const [xTickStep, yTickStep] = tickStep;
|
|
29188
|
+
|
|
29189
|
+
// Generate the tick locations & labels for the x and y axes
|
|
29358
29190
|
const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
|
|
29359
29191
|
const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
|
|
29360
29192
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29361
|
-
className: "axis-ticks"
|
|
29193
|
+
className: "axis-ticks",
|
|
29194
|
+
role: "presentation"
|
|
29195
|
+
}, /*#__PURE__*/React__namespace.createElement("g", {
|
|
29196
|
+
className: "y-axis-ticks"
|
|
29362
29197
|
}, yGridTicks.map(y => {
|
|
29363
29198
|
return /*#__PURE__*/React__namespace.createElement(YGridTick, {
|
|
29364
29199
|
y: y,
|
|
29365
29200
|
key: `y-grid-tick-${y}`,
|
|
29366
|
-
|
|
29201
|
+
range: range
|
|
29367
29202
|
});
|
|
29368
|
-
}),
|
|
29203
|
+
})), /*#__PURE__*/React__namespace.createElement("g", {
|
|
29204
|
+
className: "x-axis-ticks"
|
|
29205
|
+
}, xGridTicks.map(x => {
|
|
29369
29206
|
return /*#__PURE__*/React__namespace.createElement(XGridTick, {
|
|
29370
29207
|
x: x,
|
|
29371
29208
|
key: `x-grid-tick-${x}`,
|
|
29372
|
-
|
|
29209
|
+
range: range
|
|
29373
29210
|
});
|
|
29374
|
-
}));
|
|
29211
|
+
})));
|
|
29375
29212
|
};
|
|
29376
29213
|
|
|
29377
29214
|
/**
|
|
@@ -29392,12 +29229,10 @@ const axisOptions = (props, axisIndex) => {
|
|
|
29392
29229
|
};
|
|
29393
29230
|
};
|
|
29394
29231
|
const Grid = props => {
|
|
29395
|
-
return props.markings === "none" ? null : /*#__PURE__*/React__namespace.createElement(
|
|
29232
|
+
return props.markings === "none" ? null : /*#__PURE__*/React__namespace.createElement(mafs.Coordinates.Cartesian, {
|
|
29396
29233
|
xAxis: axisOptions(props, X),
|
|
29397
29234
|
yAxis: axisOptions(props, Y)
|
|
29398
|
-
})
|
|
29399
|
-
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
29400
|
-
props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)));
|
|
29235
|
+
});
|
|
29401
29236
|
};
|
|
29402
29237
|
|
|
29403
29238
|
// Displays a stylized open eye: lint warnings are visible
|
|
@@ -32330,11 +32165,31 @@ const MafsGraph = props => {
|
|
|
32330
32165
|
state,
|
|
32331
32166
|
dispatch,
|
|
32332
32167
|
labels,
|
|
32333
|
-
readOnly
|
|
32168
|
+
readOnly,
|
|
32169
|
+
fullGraphAriaLabel,
|
|
32170
|
+
fullGraphAriaDescription
|
|
32334
32171
|
} = props;
|
|
32335
32172
|
const [width, height] = props.box;
|
|
32336
32173
|
const tickStep = props.step;
|
|
32174
|
+
const uniqueId = React__namespace.useId();
|
|
32175
|
+
const descriptionId = `interactive-graph-description-${uniqueId}`;
|
|
32337
32176
|
const graphRef = React__namespace.useRef(null);
|
|
32177
|
+
|
|
32178
|
+
// Set up the SVG attributes for the nested SVGs that help lock
|
|
32179
|
+
// the grid and graph elements to the bounds of the graph.
|
|
32180
|
+
const {
|
|
32181
|
+
viewboxX,
|
|
32182
|
+
viewboxY
|
|
32183
|
+
} = calculateNestedSVGCoords(state.range, width, height);
|
|
32184
|
+
const viewBox = `${viewboxX} ${viewboxY} ${width} ${height}`;
|
|
32185
|
+
const nestedSVGAttributes = {
|
|
32186
|
+
width,
|
|
32187
|
+
height,
|
|
32188
|
+
viewBox,
|
|
32189
|
+
preserveAspectRatio: "xMidYMin",
|
|
32190
|
+
x: viewboxX,
|
|
32191
|
+
y: viewboxY
|
|
32192
|
+
};
|
|
32338
32193
|
return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
|
|
32339
32194
|
value: {
|
|
32340
32195
|
range: state.range,
|
|
@@ -32356,7 +32211,7 @@ const MafsGraph = props => {
|
|
|
32356
32211
|
padding: "25px 25px 0 0",
|
|
32357
32212
|
boxSizing: "content-box",
|
|
32358
32213
|
marginLeft: "20px",
|
|
32359
|
-
marginBottom: "
|
|
32214
|
+
marginBottom: "30px",
|
|
32360
32215
|
pointerEvents: props.static ? "none" : "auto",
|
|
32361
32216
|
userSelect: "none",
|
|
32362
32217
|
width,
|
|
@@ -32368,9 +32223,19 @@ const MafsGraph = props => {
|
|
|
32368
32223
|
graphRef.current?.focus();
|
|
32369
32224
|
}
|
|
32370
32225
|
},
|
|
32226
|
+
"aria-label": fullGraphAriaLabel,
|
|
32227
|
+
"aria-describedby": fullGraphAriaDescription ? descriptionId : undefined,
|
|
32371
32228
|
ref: graphRef,
|
|
32372
32229
|
tabIndex: 0
|
|
32373
|
-
}, /*#__PURE__*/React__namespace.createElement(
|
|
32230
|
+
}, fullGraphAriaDescription && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
32231
|
+
id: descriptionId,
|
|
32232
|
+
tabIndex: -1,
|
|
32233
|
+
style: {
|
|
32234
|
+
width: 0,
|
|
32235
|
+
height: 0,
|
|
32236
|
+
overflow: "hidden"
|
|
32237
|
+
}
|
|
32238
|
+
}, fullGraphAriaDescription), /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
|
|
32374
32239
|
box: props.box,
|
|
32375
32240
|
backgroundImage: props.backgroundImage
|
|
32376
32241
|
}), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
|
|
@@ -32381,7 +32246,7 @@ const MafsGraph = props => {
|
|
|
32381
32246
|
bottom: 0,
|
|
32382
32247
|
left: 0
|
|
32383
32248
|
}
|
|
32384
|
-
}, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null)
|
|
32249
|
+
}, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null)), /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
|
|
32385
32250
|
preserveAspectRatio: false,
|
|
32386
32251
|
viewBox: {
|
|
32387
32252
|
x: state.range[X],
|
|
@@ -32392,19 +32257,22 @@ const MafsGraph = props => {
|
|
|
32392
32257
|
zoom: false,
|
|
32393
32258
|
width: width,
|
|
32394
32259
|
height: height
|
|
32395
|
-
}, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement(Grid, {
|
|
32396
|
-
tickStep: props.step,
|
|
32260
|
+
}, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, /*#__PURE__*/React__namespace.createElement(Grid, {
|
|
32397
32261
|
gridStep: props.gridStep,
|
|
32398
32262
|
range: state.range,
|
|
32399
32263
|
containerSizeClass: props.containerSizeClass,
|
|
32400
|
-
markings: props.markings
|
|
32401
|
-
|
|
32264
|
+
markings: props.markings,
|
|
32265
|
+
width: width,
|
|
32266
|
+
height: height
|
|
32267
|
+
})),
|
|
32268
|
+
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
32269
|
+
props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
|
|
32402
32270
|
lockedFigures: props.lockedFigures,
|
|
32403
32271
|
range: state.range
|
|
32404
32272
|
}), props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
|
|
32405
32273
|
state,
|
|
32406
32274
|
dispatch
|
|
32407
|
-
})))), renderGraphControls({
|
|
32275
|
+
}))))), renderGraphControls({
|
|
32408
32276
|
state,
|
|
32409
32277
|
dispatch
|
|
32410
32278
|
})));
|
|
@@ -32440,6 +32308,56 @@ const renderGraphControls = props => {
|
|
|
32440
32308
|
return null;
|
|
32441
32309
|
}
|
|
32442
32310
|
};
|
|
32311
|
+
|
|
32312
|
+
// Calculate the difference between the min and max values of a range
|
|
32313
|
+
const getRangeDiff = range => {
|
|
32314
|
+
const [min, max] = range;
|
|
32315
|
+
return Math.abs(max - min);
|
|
32316
|
+
};
|
|
32317
|
+
|
|
32318
|
+
// We need to adjust the nested SVG viewbox x and Y values based on the range of the graph in order
|
|
32319
|
+
// to ensure that the graph is sized and positioned correctly within the Mafs SVG and the clipping mask.
|
|
32320
|
+
// Exported for testing.
|
|
32321
|
+
const calculateNestedSVGCoords = (range, width, height) => {
|
|
32322
|
+
// X RANGE
|
|
32323
|
+
let viewboxX = 0; // When xMin is 0, we want to use 0 as the viewboxX value
|
|
32324
|
+
const totalXRange = getRangeDiff(range[X]);
|
|
32325
|
+
const gridCellWidth = width / totalXRange;
|
|
32326
|
+
const minX = range[X][MIN];
|
|
32327
|
+
|
|
32328
|
+
// If xMin is entirely positive, we need to adjust the
|
|
32329
|
+
// viewboxX to be the grid cell width multiplied by xMin
|
|
32330
|
+
if (minX > 0) {
|
|
32331
|
+
viewboxX = gridCellWidth * Math.abs(minX);
|
|
32332
|
+
}
|
|
32333
|
+
// If xMin is negative, we need to adjust the viewboxX to be
|
|
32334
|
+
// the negative value of the grid cell width multiplied by xMin
|
|
32335
|
+
if (minX < 0) {
|
|
32336
|
+
viewboxX = -gridCellWidth * Math.abs(minX);
|
|
32337
|
+
}
|
|
32338
|
+
|
|
32339
|
+
// Y RANGE
|
|
32340
|
+
let viewboxY = -height; // When yMin is 0, we want to use the negative value of the graph height
|
|
32341
|
+
const totalYRange = getRangeDiff(range[Y]);
|
|
32342
|
+
const gridCellHeight = height / totalYRange;
|
|
32343
|
+
const minY = range[Y][MIN];
|
|
32344
|
+
|
|
32345
|
+
// If the y range is entirely positive, we want a negative sum of the
|
|
32346
|
+
// height and the gridcell height multiplied by the absolute value of yMin
|
|
32347
|
+
if (minY > 0) {
|
|
32348
|
+
viewboxY = -height - gridCellHeight * Math.abs(minY);
|
|
32349
|
+
}
|
|
32350
|
+
|
|
32351
|
+
// If the yMin is negative, we want to multiply the gridcell height
|
|
32352
|
+
// by the absolute value of yMin, and subtract the full height of the graph
|
|
32353
|
+
if (minY < 0) {
|
|
32354
|
+
viewboxY = gridCellHeight * Math.abs(minY) - height;
|
|
32355
|
+
}
|
|
32356
|
+
return {
|
|
32357
|
+
viewboxX,
|
|
32358
|
+
viewboxY
|
|
32359
|
+
};
|
|
32360
|
+
};
|
|
32443
32361
|
const renderGraph = props => {
|
|
32444
32362
|
const {
|
|
32445
32363
|
state,
|
|
@@ -45054,7 +44972,7 @@ const init = function (options) {
|
|
|
45054
44972
|
|
|
45055
44973
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45056
44974
|
const libName = "@khanacademy/perseus";
|
|
45057
|
-
const libVersion = "
|
|
44975
|
+
const libVersion = "33.1.0";
|
|
45058
44976
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45059
44977
|
|
|
45060
44978
|
const apiVersion = {
|
|
@@ -45948,128 +45866,6 @@ const ref = /*#__PURE__*/React__namespace.forwardRef((props, ref) => /*#__PURE__
|
|
|
45948
45866
|
}));
|
|
45949
45867
|
}));
|
|
45950
45868
|
|
|
45951
|
-
class ArrowPicker extends React__namespace.Component {
|
|
45952
|
-
static defaultProps = {
|
|
45953
|
-
value: ""
|
|
45954
|
-
};
|
|
45955
|
-
render() {
|
|
45956
|
-
return /*#__PURE__*/React__namespace.createElement(ButtonGroup, {
|
|
45957
|
-
value: this.props.value,
|
|
45958
|
-
allowEmpty: false,
|
|
45959
|
-
buttons: [{
|
|
45960
|
-
value: "",
|
|
45961
|
-
content: /*#__PURE__*/React__namespace.createElement("span", null, "\u2014")
|
|
45962
|
-
}, {
|
|
45963
|
-
value: "->",
|
|
45964
|
-
content: /*#__PURE__*/React__namespace.createElement("span", null, "\u2192")
|
|
45965
|
-
}
|
|
45966
|
-
/*
|
|
45967
|
-
TODO(eater): fix khan-exercises so these are supported
|
|
45968
|
-
{value: "<-", content: <span>←</span>},
|
|
45969
|
-
{value: "<->", content: <span>↔</span>}
|
|
45970
|
-
*/],
|
|
45971
|
-
|
|
45972
|
-
onChange: this.props.onChange
|
|
45973
|
-
});
|
|
45974
|
-
}
|
|
45975
|
-
}
|
|
45976
|
-
|
|
45977
|
-
const COLORS = [KhanColors.BLACK, KhanColors.BLUE, KhanColors.GREEN, KhanColors.PINK, KhanColors.PURPLE, KhanColors.RED, KhanColors.GRAY];
|
|
45978
|
-
const LIGHT_COLORS = [KhanColors.LIGHT_BLUE, KhanColors.LIGHT_ORANGE, KhanColors.LIGHT_PINK, KhanColors.LIGHT_GREEN, KhanColors.LIGHT_PURPLE, KhanColors.LIGHT_RED, "#fff"];
|
|
45979
|
-
class ColorPicker extends React__namespace.Component {
|
|
45980
|
-
static defaultProps = {
|
|
45981
|
-
value: KhanColors.BLACK,
|
|
45982
|
-
lightColors: false
|
|
45983
|
-
};
|
|
45984
|
-
render() {
|
|
45985
|
-
const colors = this.props.lightColors ? LIGHT_COLORS : COLORS;
|
|
45986
|
-
return /*#__PURE__*/React__namespace.createElement(ButtonGroup, {
|
|
45987
|
-
value: this.props.value,
|
|
45988
|
-
allowEmpty: false,
|
|
45989
|
-
buttons: ___default["default"].map(colors, color => {
|
|
45990
|
-
return {
|
|
45991
|
-
value: color,
|
|
45992
|
-
content: /*#__PURE__*/React__namespace.createElement("span", null, /*#__PURE__*/React__namespace.createElement("span", {
|
|
45993
|
-
className: "colorpicker-circle",
|
|
45994
|
-
style: {
|
|
45995
|
-
background: color
|
|
45996
|
-
}
|
|
45997
|
-
}), "\xA0")
|
|
45998
|
-
};
|
|
45999
|
-
}),
|
|
46000
|
-
onChange: this.props.onChange
|
|
46001
|
-
});
|
|
46002
|
-
}
|
|
46003
|
-
}
|
|
46004
|
-
|
|
46005
|
-
class DashPicker extends React__namespace.Component {
|
|
46006
|
-
static defaultProps = {
|
|
46007
|
-
value: ""
|
|
46008
|
-
};
|
|
46009
|
-
render() {
|
|
46010
|
-
return /*#__PURE__*/React__namespace.createElement(ButtonGroup, {
|
|
46011
|
-
value: this.props.value,
|
|
46012
|
-
allowEmpty: false,
|
|
46013
|
-
buttons: [{
|
|
46014
|
-
value: "",
|
|
46015
|
-
content: /*#__PURE__*/React__namespace.createElement("span", null, "\u2014")
|
|
46016
|
-
}, {
|
|
46017
|
-
value: "-",
|
|
46018
|
-
content: /*#__PURE__*/React__namespace.createElement("span", null, "\u2013\u2013\u2013")
|
|
46019
|
-
}, {
|
|
46020
|
-
value: "- ",
|
|
46021
|
-
content: /*#__PURE__*/React__namespace.createElement("span", null, "\u2013\xA0\xA0\u2013")
|
|
46022
|
-
}, {
|
|
46023
|
-
value: ".",
|
|
46024
|
-
content: /*#__PURE__*/React__namespace.createElement("span", null, "\xB7\xB7\xB7\xB7")
|
|
46025
|
-
}, {
|
|
46026
|
-
value: ". ",
|
|
46027
|
-
content: /*#__PURE__*/React__namespace.createElement("span", null, "\xB7 \xB7 \xB7")
|
|
46028
|
-
}],
|
|
46029
|
-
onChange: this.props.onChange
|
|
46030
|
-
});
|
|
46031
|
-
}
|
|
46032
|
-
}
|
|
46033
|
-
|
|
46034
|
-
/* eslint-disable jsx-a11y/anchor-is-valid */
|
|
46035
|
-
class ElementContainer extends React__namespace.Component {
|
|
46036
|
-
static defaultProps = {
|
|
46037
|
-
initiallyVisible: false,
|
|
46038
|
-
title: "More"
|
|
46039
|
-
};
|
|
46040
|
-
constructor(props) {
|
|
46041
|
-
super(props);
|
|
46042
|
-
this.state = {
|
|
46043
|
-
show: props.initiallyVisible
|
|
46044
|
-
};
|
|
46045
|
-
}
|
|
46046
|
-
toggle = e => {
|
|
46047
|
-
e.preventDefault();
|
|
46048
|
-
this.setState({
|
|
46049
|
-
show: !this.state.show
|
|
46050
|
-
});
|
|
46051
|
-
};
|
|
46052
|
-
render() {
|
|
46053
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
46054
|
-
className: "perseus-interaction-element"
|
|
46055
|
-
}, /*#__PURE__*/React__namespace.createElement("a", {
|
|
46056
|
-
href: "#",
|
|
46057
|
-
className: "perseus-interaction-element-title " + (this.state.show ? "open" : "closed"),
|
|
46058
|
-
onClick: this.toggle
|
|
46059
|
-
}, this.state.show ? /*#__PURE__*/React__namespace.createElement(InlineIcon, iconChevronDown) : /*#__PURE__*/React__namespace.createElement(InlineIcon, iconChevronRight), this.props.title), /*#__PURE__*/React__namespace.createElement("div", {
|
|
46060
|
-
className: "perseus-interaction-element-content " + (this.state.show ? "enter" : "leave")
|
|
46061
|
-
}, this.props.children, (this.props.onUp != null || this.props.onDown != null || this.props.onDelete != null) && /*#__PURE__*/React__namespace.createElement("div", {
|
|
46062
|
-
className: "edit-controls"
|
|
46063
|
-
}, this.props.onUp != null && /*#__PURE__*/React__namespace.createElement("button", {
|
|
46064
|
-
onClick: this.props.onUp
|
|
46065
|
-
}, /*#__PURE__*/React__namespace.createElement(InlineIcon, iconCircleArrowUp)), this.props.onDown != null && /*#__PURE__*/React__namespace.createElement("button", {
|
|
46066
|
-
onClick: this.props.onDown
|
|
46067
|
-
}, /*#__PURE__*/React__namespace.createElement(InlineIcon, iconCircleArrowDown)), this.props.onDelete != null && /*#__PURE__*/React__namespace.createElement("button", {
|
|
46068
|
-
onClick: this.props.onDelete
|
|
46069
|
-
}, /*#__PURE__*/React__namespace.createElement(InlineIcon, iconTrash)))));
|
|
46070
|
-
}
|
|
46071
|
-
}
|
|
46072
|
-
|
|
46073
45869
|
const devices = {
|
|
46074
45870
|
PHONE: "phone",
|
|
46075
45871
|
TABLET: "tablet",
|
|
@@ -48655,17 +48451,13 @@ const EditorJsonify = {
|
|
|
48655
48451
|
};
|
|
48656
48452
|
|
|
48657
48453
|
exports.ApiOptions = ApiOptions;
|
|
48658
|
-
exports.ArrowPicker = ArrowPicker;
|
|
48659
48454
|
exports.ArticleRenderer = ArticleRenderer;
|
|
48660
48455
|
exports.BaseRadio = BaseRadio;
|
|
48661
48456
|
exports.Categorizer = Categorizer$1;
|
|
48662
48457
|
exports.Changeable = changeable;
|
|
48663
48458
|
exports.ClassNames = ClassNames;
|
|
48664
|
-
exports.ColorPicker = ColorPicker;
|
|
48665
|
-
exports.DashPicker = DashPicker;
|
|
48666
48459
|
exports.Dependencies = dependencies;
|
|
48667
48460
|
exports.EditorJsonify = EditorJsonify;
|
|
48668
|
-
exports.ElementContainer = ElementContainer;
|
|
48669
48461
|
exports.Expression = Expression;
|
|
48670
48462
|
exports.GrapherUtil = util;
|
|
48671
48463
|
exports.GrapherWidget = Grapher$1;
|