@khanacademy/perseus 33.0.0 → 33.2.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 +393 -468
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +15 -44
- package/dist/index.js +394 -471
- 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/util/colors.d.ts +5 -13
- 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/interactive-graphs/types.d.ts +1 -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/index.js
CHANGED
|
@@ -8780,10 +8780,10 @@ var Raphael$1 = (function () {
|
|
|
8780
8780
|
* provided on KhanUtil.
|
|
8781
8781
|
*/
|
|
8782
8782
|
const KhanColors = {
|
|
8783
|
-
BLUE:
|
|
8783
|
+
BLUE: wonderBlocksTokens.color.blue,
|
|
8784
8784
|
ORANGE: "#FFA500",
|
|
8785
8785
|
PINK: "#FF00AF",
|
|
8786
|
-
GREEN:
|
|
8786
|
+
GREEN: wonderBlocksTokens.color.green,
|
|
8787
8787
|
PURPLE: "#9D38BD",
|
|
8788
8788
|
RED: "#DF0030",
|
|
8789
8789
|
GRAY: "gray",
|
|
@@ -8859,11 +8859,9 @@ const KhanColors = {
|
|
|
8859
8859
|
// of any redesigns.
|
|
8860
8860
|
_BACKGROUND: "#FDFDFD",
|
|
8861
8861
|
// TODO(eater): Get rid of this altogether.
|
|
8862
|
-
INTERACTING:
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
// KA_GREEN
|
|
8866
|
-
DYNAMIC: "#6495ED" // BLUE
|
|
8862
|
+
INTERACTING: wonderBlocksTokens.color.green,
|
|
8863
|
+
INTERACTIVE: wonderBlocksTokens.color.green,
|
|
8864
|
+
DYNAMIC: wonderBlocksTokens.color.blue
|
|
8867
8865
|
};
|
|
8868
8866
|
|
|
8869
8867
|
// DrawingTransform is responsible for transforming from "math coordinates"
|
|
@@ -10147,21 +10145,50 @@ const setLabelMargins = function (span, size) {
|
|
|
10147
10145
|
marginTop: -height / 2 - y * scale
|
|
10148
10146
|
});
|
|
10149
10147
|
} else {
|
|
10150
|
-
|
|
10148
|
+
// We are using jQuery to collect information and calculate a scale
|
|
10149
|
+
// since we don't have a way to pass it to this function.
|
|
10150
|
+
// We need the width of the container in order to calculate the scale to apply to the label.
|
|
10151
|
+
// Unfortunately, the DOM tree leading to the label is not consistent.
|
|
10152
|
+
// Therefore, we need to check different elements and different style properties to get the width of the container.
|
|
10153
|
+
// Some graphie labels exist within an ".svg-image" container, and others do not.
|
|
10154
|
+
// All graphie labels have a ".graphie" container.
|
|
10155
|
+
// When a label has an ".svg-image" container,
|
|
10156
|
+
// the width information in the ".graphie" element is expressed as a percentage instead of pixels,
|
|
10157
|
+
// so we can't use it.
|
|
10158
|
+
// Instead, the ".svg-image" container can be used to get the width information.
|
|
10159
|
+
// The following lines determine which container element to use for the width measurement.
|
|
10160
|
+
const $svgImage = $span.closest(".svg-image");
|
|
10161
|
+
const $graphie = $span.closest(".graphie");
|
|
10162
|
+
// If an ".svg-image" container exists in the DOM tree, then use it, otherwise use the ".graphie" container.
|
|
10163
|
+
const $container = $svgImage.length > 0 ? $svgImage : $graphie;
|
|
10164
|
+
|
|
10151
10165
|
// Ensuring that the line-height of the text doesn't throw off placement of the text element.
|
|
10152
10166
|
// Inherited line-height values can really mess up placement.
|
|
10153
10167
|
$container.css("line-height", "normal");
|
|
10154
10168
|
|
|
10155
|
-
// The
|
|
10156
|
-
//
|
|
10157
|
-
const
|
|
10169
|
+
// The expected width of the graphie is found in the "max-width" property on ".svg-image" containers,
|
|
10170
|
+
// and is found in the "width" property on ".graphie" containers.
|
|
10171
|
+
const widthValues = $container.css(["max-width", "width"]) ?? {
|
|
10172
|
+
// The browser will always return a value, but tests won't, so ensuring something is returned.
|
|
10173
|
+
"max-width": "0px"
|
|
10174
|
+
};
|
|
10175
|
+
const expectedWidth =
|
|
10176
|
+
// Verified in Chrome, Firefox, and Safari - all return the same "none" value if the property does not exist.
|
|
10177
|
+
widthValues["max-width"] !== "none" ? widthValues["max-width"] : widthValues["width"];
|
|
10158
10178
|
// We can calculate the true scale of the graphie by taking actual width and dividing by the expected width.
|
|
10159
|
-
// NOTE: Using '
|
|
10160
|
-
|
|
10179
|
+
// NOTE: Using 'replace' to remove the "px" unit from the end of the expected width value.
|
|
10180
|
+
let scale = ($container.width() ?? 0) / parseInt(expectedWidth.replace(/px$/, "")) * 100;
|
|
10181
|
+
// If something failed in the calculation, then default to 100% scale.
|
|
10182
|
+
if (isNaN(scale)) {
|
|
10183
|
+
scale = 100;
|
|
10184
|
+
} else if (scale === 0) {
|
|
10185
|
+
scale = 100;
|
|
10186
|
+
}
|
|
10161
10187
|
|
|
10162
10188
|
// Any padding needs to be scaled accordingly.
|
|
10163
|
-
const
|
|
10164
|
-
const
|
|
10189
|
+
const padding = $span.css("padding") ?? "0px";
|
|
10190
|
+
const currentPadding = padding !== "none" ? padding : "0px";
|
|
10191
|
+
const newPadding = Math.round(parseInt(currentPadding.replace(/px$/, "")) * scale) / 100;
|
|
10165
10192
|
|
|
10166
10193
|
// "multipliers" basically move the position of the text by using 1, 0, -1
|
|
10167
10194
|
// 'margin-left' and 'margin-top' are used to position the text.
|
|
@@ -28617,6 +28644,95 @@ class Graph extends React__namespace.Component {
|
|
|
28617
28644
|
}
|
|
28618
28645
|
}
|
|
28619
28646
|
|
|
28647
|
+
function pathBuilder() {
|
|
28648
|
+
return new PathBuilder();
|
|
28649
|
+
}
|
|
28650
|
+
class PathBuilder {
|
|
28651
|
+
path = [];
|
|
28652
|
+
scaleFactor = 1;
|
|
28653
|
+
build() {
|
|
28654
|
+
return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
|
|
28655
|
+
}
|
|
28656
|
+
|
|
28657
|
+
// add a move (M) command to the path
|
|
28658
|
+
move(x, y) {
|
|
28659
|
+
this.path.push({
|
|
28660
|
+
action: "M",
|
|
28661
|
+
args: [x, y]
|
|
28662
|
+
});
|
|
28663
|
+
return this;
|
|
28664
|
+
}
|
|
28665
|
+
line(x, y) {
|
|
28666
|
+
this.path.push({
|
|
28667
|
+
action: "L",
|
|
28668
|
+
args: [x, y]
|
|
28669
|
+
});
|
|
28670
|
+
return this;
|
|
28671
|
+
}
|
|
28672
|
+
circularArc(radius, toX, toY) {
|
|
28673
|
+
let {
|
|
28674
|
+
sweep = false,
|
|
28675
|
+
largeArc = false
|
|
28676
|
+
} = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
28677
|
+
this.path.push({
|
|
28678
|
+
action: "A",
|
|
28679
|
+
args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
|
|
28680
|
+
});
|
|
28681
|
+
return this;
|
|
28682
|
+
}
|
|
28683
|
+
|
|
28684
|
+
// add a curve (C) command to the path
|
|
28685
|
+
curve(control1X, control1Y, control2X, control2Y, endX, endY) {
|
|
28686
|
+
this.path.push({
|
|
28687
|
+
action: "C",
|
|
28688
|
+
args: [control1X, control1Y, control2X, control2Y, endX, endY]
|
|
28689
|
+
});
|
|
28690
|
+
return this;
|
|
28691
|
+
}
|
|
28692
|
+
|
|
28693
|
+
// Scale all coordinates of the path by a factor
|
|
28694
|
+
scale(factor) {
|
|
28695
|
+
this.scaleFactor *= factor;
|
|
28696
|
+
return this;
|
|
28697
|
+
}
|
|
28698
|
+
}
|
|
28699
|
+
function commandToString(command) {
|
|
28700
|
+
return `${command.action}${command.args.join(" ")}`;
|
|
28701
|
+
}
|
|
28702
|
+
function scaleCommandBy(scaleFactor) {
|
|
28703
|
+
return command => {
|
|
28704
|
+
switch (command.action) {
|
|
28705
|
+
case "A":
|
|
28706
|
+
// TODO(benchristel): do we want to refactor this switch to
|
|
28707
|
+
// use polymorphism?
|
|
28708
|
+
// Arc command
|
|
28709
|
+
return {
|
|
28710
|
+
...command,
|
|
28711
|
+
args: [command.args[0] * scaleFactor,
|
|
28712
|
+
// x radius
|
|
28713
|
+
command.args[1] * scaleFactor,
|
|
28714
|
+
// y radius
|
|
28715
|
+
command.args[2],
|
|
28716
|
+
// rotation
|
|
28717
|
+
command.args[3],
|
|
28718
|
+
// largeArc flag
|
|
28719
|
+
command.args[4],
|
|
28720
|
+
// sweep flag
|
|
28721
|
+
command.args[5] * scaleFactor,
|
|
28722
|
+
// end x
|
|
28723
|
+
command.args[6] * scaleFactor // end y
|
|
28724
|
+
]
|
|
28725
|
+
};
|
|
28726
|
+
|
|
28727
|
+
default:
|
|
28728
|
+
return {
|
|
28729
|
+
...command,
|
|
28730
|
+
args: command.args.map(c => c * scaleFactor)
|
|
28731
|
+
};
|
|
28732
|
+
}
|
|
28733
|
+
};
|
|
28734
|
+
}
|
|
28735
|
+
|
|
28620
28736
|
// Restricts `value` to be between `min` and `max`.
|
|
28621
28737
|
function clamp(value, min, max) {
|
|
28622
28738
|
if (value < min) {
|
|
@@ -28846,380 +28962,6 @@ function pixelsToVectors(pixels, graphState) {
|
|
|
28846
28962
|
});
|
|
28847
28963
|
}
|
|
28848
28964
|
|
|
28849
|
-
function AxisLabels() {
|
|
28850
|
-
const {
|
|
28851
|
-
range,
|
|
28852
|
-
labels,
|
|
28853
|
-
width,
|
|
28854
|
-
height
|
|
28855
|
-
} = useGraphConfig();
|
|
28856
|
-
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28857
|
-
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28858
|
-
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
28859
|
-
const graphInfo = {
|
|
28860
|
-
range,
|
|
28861
|
-
width,
|
|
28862
|
-
height
|
|
28863
|
-
};
|
|
28864
|
-
const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
|
|
28865
|
-
// The default location for the y-axis-label is at the maximum y point
|
|
28866
|
-
// This is just underneath the tick line for that point. The -24 moves the
|
|
28867
|
-
// label up one grid square, so it sits on top of the graph
|
|
28868
|
-
const [x2, y2] = mafs.vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
|
|
28869
|
-
const {
|
|
28870
|
-
TeX
|
|
28871
|
-
} = getDependencies();
|
|
28872
|
-
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("span", {
|
|
28873
|
-
style: {
|
|
28874
|
-
position: "absolute",
|
|
28875
|
-
left: x1,
|
|
28876
|
-
top: y1,
|
|
28877
|
-
fontSize: "14px",
|
|
28878
|
-
transform: "translate(7px, -50%)"
|
|
28879
|
-
}
|
|
28880
|
-
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
28881
|
-
style: {
|
|
28882
|
-
position: "absolute",
|
|
28883
|
-
left: x2,
|
|
28884
|
-
top: y2,
|
|
28885
|
-
fontSize: "14px",
|
|
28886
|
-
transform: "translate(-50%, 0px)"
|
|
28887
|
-
}
|
|
28888
|
-
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, yAxisLabelText)));
|
|
28889
|
-
}
|
|
28890
|
-
|
|
28891
|
-
// Universal padding for the axis ticks to ensure that the labels
|
|
28892
|
-
// have enough space to render without overlapping the tick lines
|
|
28893
|
-
const tickPadding = 5;
|
|
28894
|
-
const AxisTickLabels = () => {
|
|
28895
|
-
const graphConfig = useGraphConfig();
|
|
28896
|
-
const {
|
|
28897
|
-
tickStep,
|
|
28898
|
-
range
|
|
28899
|
-
} = graphConfig;
|
|
28900
|
-
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
28901
|
-
const [xTickStep, yTickStep] = tickStep;
|
|
28902
|
-
const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
|
|
28903
|
-
const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
|
|
28904
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
28905
|
-
className: "axis-tick-labels"
|
|
28906
|
-
}, /*#__PURE__*/React__namespace.createElement(YGridAxis, {
|
|
28907
|
-
axisTicks: yGridTicks,
|
|
28908
|
-
graphConfig: graphConfig
|
|
28909
|
-
}), /*#__PURE__*/React__namespace.createElement(XGridAxis, {
|
|
28910
|
-
axisTicks: xGridTicks,
|
|
28911
|
-
graphConfig: graphConfig
|
|
28912
|
-
}));
|
|
28913
|
-
};
|
|
28914
|
-
|
|
28915
|
-
// Generate the necessary styles and ticks for the y-axis tick labels container
|
|
28916
|
-
const YGridAxis = props => {
|
|
28917
|
-
const {
|
|
28918
|
-
axisTicks,
|
|
28919
|
-
graphConfig
|
|
28920
|
-
} = props;
|
|
28921
|
-
const {
|
|
28922
|
-
width,
|
|
28923
|
-
height
|
|
28924
|
-
} = graphConfig;
|
|
28925
|
-
|
|
28926
|
-
// First get the label height by calculating the difference between the first two y-axis labels
|
|
28927
|
-
const yTopPoint = pointToPixel([0, axisTicks[0]], graphConfig);
|
|
28928
|
-
const ySecondPoint = pointToPixel([0, axisTicks[1]], graphConfig);
|
|
28929
|
-
const yLabelHeight = ySecondPoint[1] - yTopPoint[1];
|
|
28930
|
-
|
|
28931
|
-
// When rendering purely positive y-axis labels, we want to adjust the left position
|
|
28932
|
-
// from the lowest calculated tick, as yMin may be negative
|
|
28933
|
-
const yLowestTick = axisTicks[axisTicks.length - 1];
|
|
28934
|
-
const yLeftAdjustment = Math.min(Math.max(yTopPoint[0] - tickPadding, 0), width);
|
|
28935
|
-
|
|
28936
|
-
// We only want to render the 0 label on the y-axis if it is left of the grid
|
|
28937
|
-
const yAxisIsLeftOfGrid = yLeftAdjustment === 0;
|
|
28938
|
-
|
|
28939
|
-
// If the y-axis is right of the graph's viewbox/range,
|
|
28940
|
-
// we want to adjust both the positioning and text alignment
|
|
28941
|
-
const yAxisRightOfGrid = yLeftAdjustment === width;
|
|
28942
|
-
|
|
28943
|
-
// Determine the default spacing for the labels based on whether the y-axis is right of the grid
|
|
28944
|
-
const defaultSpacing = yAxisRightOfGrid ? 0.5 : 0;
|
|
28945
|
-
|
|
28946
|
-
// We also want to adjust the spacing if there are no negative y-axis labels as the extra space is not needed
|
|
28947
|
-
const marginSpacing = yLowestTick >= 0 ? defaultSpacing * 2 : defaultSpacing;
|
|
28948
|
-
|
|
28949
|
-
// Determine the text alignment based on whether the y-axis is right of the grid
|
|
28950
|
-
// We need to cast this as a const as it is used in the inline styles
|
|
28951
|
-
const textAlign = yAxisRightOfGrid ? "left" : "right";
|
|
28952
|
-
const yAxisStyles = {
|
|
28953
|
-
left: `calc(${yLeftAdjustment}px - ${marginSpacing}em)`,
|
|
28954
|
-
top: `calc(${yTopPoint[1]}px - 0.5em)`,
|
|
28955
|
-
height,
|
|
28956
|
-
textAlign,
|
|
28957
|
-
"--y-axis-label-height": yLabelHeight + "px"
|
|
28958
|
-
};
|
|
28959
|
-
const classNames = yAxisRightOfGrid ? "y-axis-tick-labels y-axis-right-of-grid" : "y-axis-tick-labels ";
|
|
28960
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
28961
|
-
className: classNames,
|
|
28962
|
-
"data-testid": "y-axis-tick-labels",
|
|
28963
|
-
style: yAxisStyles
|
|
28964
|
-
}, axisTicks.map(label => {
|
|
28965
|
-
return /*#__PURE__*/React__namespace.createElement(AxisTickLabel, {
|
|
28966
|
-
label: label,
|
|
28967
|
-
axis: "y",
|
|
28968
|
-
key: `y-grid-tick-${label}`,
|
|
28969
|
-
graphConfig: graphConfig,
|
|
28970
|
-
axisOutOfBounds: yAxisIsLeftOfGrid
|
|
28971
|
-
});
|
|
28972
|
-
}));
|
|
28973
|
-
};
|
|
28974
|
-
|
|
28975
|
-
// Generate the necessary styles and ticks for the x-axis tick labels container
|
|
28976
|
-
const XGridAxis = props => {
|
|
28977
|
-
const {
|
|
28978
|
-
axisTicks,
|
|
28979
|
-
graphConfig
|
|
28980
|
-
} = props;
|
|
28981
|
-
const {
|
|
28982
|
-
width,
|
|
28983
|
-
height
|
|
28984
|
-
} = graphConfig;
|
|
28985
|
-
|
|
28986
|
-
// Determine label width by calculating the difference between the first two x-axis labels
|
|
28987
|
-
const xFirstPoint = pointToPixel([axisTicks[0], 0], graphConfig);
|
|
28988
|
-
const xSecondPoint = pointToPixel([axisTicks[1], 0], graphConfig);
|
|
28989
|
-
const xLabelWidth = Math.abs(xSecondPoint[0] - xFirstPoint[0]);
|
|
28990
|
-
|
|
28991
|
-
// Determine the top position of the x-axis labels based on
|
|
28992
|
-
// whether the x-axis is above, within, or below the graph
|
|
28993
|
-
const xAxisAboveGraph = xFirstPoint[1] < 0;
|
|
28994
|
-
const xTopAdjustment = xAxisAboveGraph ? 0 : Math.min(xFirstPoint[1] + tickPadding, height);
|
|
28995
|
-
|
|
28996
|
-
// Determine whether the x-axis is outside of the graph's viewbox/range
|
|
28997
|
-
// This is used to conditionally render the 0 label on the x-axis
|
|
28998
|
-
const xAxisOutOfBounds = xTopAdjustment === height || xAxisAboveGraph;
|
|
28999
|
-
|
|
29000
|
-
// Determine the available space to the right of the x-axis labels
|
|
29001
|
-
const availableSpaceRight = width - xFirstPoint[0];
|
|
29002
|
-
|
|
29003
|
-
// Calculate the total combined width of ALL the x-axis labels
|
|
29004
|
-
const axisTicksWidth = axisTicks.length * xLabelWidth;
|
|
29005
|
-
|
|
29006
|
-
// Determine if we need to increase the width of the axis past the graph width
|
|
29007
|
-
const calculatedWidth = axisTicksWidth > width ? axisTicksWidth : width;
|
|
29008
|
-
|
|
29009
|
-
// Determine the right adjustment for the x-axis labels based on the available space
|
|
29010
|
-
// We're adjusting the right as the flex-direction is row-reverse
|
|
29011
|
-
const xRightAdjustment = availableSpaceRight - xLabelWidth / 2;
|
|
29012
|
-
const xAxisStyles = {
|
|
29013
|
-
top: xTopAdjustment,
|
|
29014
|
-
right: xRightAdjustment,
|
|
29015
|
-
width: calculatedWidth,
|
|
29016
|
-
"--x-axis-label-width": xLabelWidth + "px"
|
|
29017
|
-
};
|
|
29018
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
29019
|
-
className: `x-axis-tick-labels ${xAxisAboveGraph && "x-axis-top-of-grid"}`,
|
|
29020
|
-
"data-testid": "x-axis-tick-labels",
|
|
29021
|
-
style: xAxisStyles
|
|
29022
|
-
}, axisTicks.map(label => {
|
|
29023
|
-
return /*#__PURE__*/React__namespace.createElement(AxisTickLabel, {
|
|
29024
|
-
axis: "x",
|
|
29025
|
-
label: label,
|
|
29026
|
-
key: `x-grid-tick-${label}`,
|
|
29027
|
-
graphConfig: graphConfig,
|
|
29028
|
-
axisOutOfBounds: xAxisOutOfBounds
|
|
29029
|
-
});
|
|
29030
|
-
}));
|
|
29031
|
-
};
|
|
29032
|
-
|
|
29033
|
-
// Generate the individual tick label elements
|
|
29034
|
-
const AxisTickLabel = _ref => {
|
|
29035
|
-
let {
|
|
29036
|
-
label,
|
|
29037
|
-
graphConfig,
|
|
29038
|
-
axisOutOfBounds,
|
|
29039
|
-
axis
|
|
29040
|
-
} = _ref;
|
|
29041
|
-
const {
|
|
29042
|
-
gridStep,
|
|
29043
|
-
tickStep
|
|
29044
|
-
} = graphConfig;
|
|
29045
|
-
// Determine the point on the axis based on the axis type
|
|
29046
|
-
const pointOnAxis = axis === "x" ? [label, 0] : [0, label];
|
|
29047
|
-
const pixelPoint = pointToPixel(pointOnAxis, graphConfig);
|
|
29048
|
-
|
|
29049
|
-
// Determine the relevant edge of the graph based on the axis type
|
|
29050
|
-
const graphEdge = axis === "x" ? graphConfig.width : graphConfig.height;
|
|
29051
|
-
|
|
29052
|
-
// Determine the vector index based on the axis type
|
|
29053
|
-
const vectorIndex = axis === "x" ? 0 : 1;
|
|
29054
|
-
|
|
29055
|
-
// Determine whether or not to render the tick label text
|
|
29056
|
-
const shouldShowLabel = showTickLabel({
|
|
29057
|
-
label,
|
|
29058
|
-
axisOutOfBounds,
|
|
29059
|
-
graphEdge,
|
|
29060
|
-
position: pixelPoint[vectorIndex],
|
|
29061
|
-
gridStep: gridStep[vectorIndex],
|
|
29062
|
-
tickStep: tickStep[vectorIndex]
|
|
29063
|
-
});
|
|
29064
|
-
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
29065
|
-
className: `${axis}-axis-tick-label`
|
|
29066
|
-
}, shouldShowLabel && label);
|
|
29067
|
-
};
|
|
29068
|
-
|
|
29069
|
-
// Determine whether or not to show the tick label text
|
|
29070
|
-
const showTickLabel = _ref2 => {
|
|
29071
|
-
let {
|
|
29072
|
-
gridStep,
|
|
29073
|
-
tickStep,
|
|
29074
|
-
label,
|
|
29075
|
-
graphEdge,
|
|
29076
|
-
position,
|
|
29077
|
-
axisOutOfBounds
|
|
29078
|
-
} = _ref2;
|
|
29079
|
-
let showLabel = true;
|
|
29080
|
-
|
|
29081
|
-
// We only want to show the initial negative tick labels (e.g. -1) on each
|
|
29082
|
-
// axis if the tickStep > gridStep, to ensure that these labels do not overlap.
|
|
29083
|
-
// e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
|
|
29084
|
-
// which allows enough room for both these tick labels to render.
|
|
29085
|
-
showLabel = tickStep > gridStep ? true : label !== -tickStep;
|
|
29086
|
-
|
|
29087
|
-
// If the tick label position is greater than the relevant edge of the graph, don't render it
|
|
29088
|
-
if (position <= -graphEdge || position >= graphEdge) {
|
|
29089
|
-
showLabel = false;
|
|
29090
|
-
}
|
|
29091
|
-
|
|
29092
|
-
// If the label is 0, we only want to show it if the axis is to the left of the graph's viewbox/range
|
|
29093
|
-
// otherwise it will overlap with graph axis labels
|
|
29094
|
-
if (label === 0 && !axisOutOfBounds) {
|
|
29095
|
-
showLabel = false;
|
|
29096
|
-
}
|
|
29097
|
-
return showLabel;
|
|
29098
|
-
};
|
|
29099
|
-
|
|
29100
|
-
// Generate the tick labels for an axis based on the tick step
|
|
29101
|
-
function generateTickLocations$1(tickStep, min, max) {
|
|
29102
|
-
const positiveTicks = [];
|
|
29103
|
-
const negativeTicks = [];
|
|
29104
|
-
const ticks = [];
|
|
29105
|
-
|
|
29106
|
-
// Add 0 if it is applicable.
|
|
29107
|
-
// This will be conditionally rendered using showTickLabel.
|
|
29108
|
-
if (min <= 0 && max >= 0) {
|
|
29109
|
-
positiveTicks.push(0);
|
|
29110
|
-
}
|
|
29111
|
-
|
|
29112
|
-
// Add ticks on the positive axis
|
|
29113
|
-
// Start at the first tick after 0 or the minimum value,
|
|
29114
|
-
// taking into account that the graph may be zoomed in on the positive axis
|
|
29115
|
-
let i = Math.max(min, 0) + tickStep;
|
|
29116
|
-
for (i; min < i && i < max; i += tickStep) {
|
|
29117
|
-
positiveTicks.push(i);
|
|
29118
|
-
}
|
|
29119
|
-
|
|
29120
|
-
// Add ticks on the negative axis
|
|
29121
|
-
// Start at the first tick before 0 or the maximum value,
|
|
29122
|
-
// taking into account that the graph may be zoomed in on the negative axis
|
|
29123
|
-
i = Math.min(max, 0) - tickStep;
|
|
29124
|
-
for (i; i > min; i -= tickStep) {
|
|
29125
|
-
negativeTicks.push(i);
|
|
29126
|
-
}
|
|
29127
|
-
|
|
29128
|
-
// Reverse the ticks so that they are in the correct render order
|
|
29129
|
-
positiveTicks.reverse();
|
|
29130
|
-
ticks.push(...positiveTicks, ...negativeTicks);
|
|
29131
|
-
return ticks;
|
|
29132
|
-
}
|
|
29133
|
-
|
|
29134
|
-
function pathBuilder() {
|
|
29135
|
-
return new PathBuilder();
|
|
29136
|
-
}
|
|
29137
|
-
class PathBuilder {
|
|
29138
|
-
path = [];
|
|
29139
|
-
scaleFactor = 1;
|
|
29140
|
-
build() {
|
|
29141
|
-
return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
|
|
29142
|
-
}
|
|
29143
|
-
|
|
29144
|
-
// add a move (M) command to the path
|
|
29145
|
-
move(x, y) {
|
|
29146
|
-
this.path.push({
|
|
29147
|
-
action: "M",
|
|
29148
|
-
args: [x, y]
|
|
29149
|
-
});
|
|
29150
|
-
return this;
|
|
29151
|
-
}
|
|
29152
|
-
line(x, y) {
|
|
29153
|
-
this.path.push({
|
|
29154
|
-
action: "L",
|
|
29155
|
-
args: [x, y]
|
|
29156
|
-
});
|
|
29157
|
-
return this;
|
|
29158
|
-
}
|
|
29159
|
-
circularArc(radius, toX, toY) {
|
|
29160
|
-
let {
|
|
29161
|
-
sweep = false,
|
|
29162
|
-
largeArc = false
|
|
29163
|
-
} = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
29164
|
-
this.path.push({
|
|
29165
|
-
action: "A",
|
|
29166
|
-
args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
|
|
29167
|
-
});
|
|
29168
|
-
return this;
|
|
29169
|
-
}
|
|
29170
|
-
|
|
29171
|
-
// add a curve (C) command to the path
|
|
29172
|
-
curve(control1X, control1Y, control2X, control2Y, endX, endY) {
|
|
29173
|
-
this.path.push({
|
|
29174
|
-
action: "C",
|
|
29175
|
-
args: [control1X, control1Y, control2X, control2Y, endX, endY]
|
|
29176
|
-
});
|
|
29177
|
-
return this;
|
|
29178
|
-
}
|
|
29179
|
-
|
|
29180
|
-
// Scale all coordinates of the path by a factor
|
|
29181
|
-
scale(factor) {
|
|
29182
|
-
this.scaleFactor *= factor;
|
|
29183
|
-
return this;
|
|
29184
|
-
}
|
|
29185
|
-
}
|
|
29186
|
-
function commandToString(command) {
|
|
29187
|
-
return `${command.action}${command.args.join(" ")}`;
|
|
29188
|
-
}
|
|
29189
|
-
function scaleCommandBy(scaleFactor) {
|
|
29190
|
-
return command => {
|
|
29191
|
-
switch (command.action) {
|
|
29192
|
-
case "A":
|
|
29193
|
-
// TODO(benchristel): do we want to refactor this switch to
|
|
29194
|
-
// use polymorphism?
|
|
29195
|
-
// Arc command
|
|
29196
|
-
return {
|
|
29197
|
-
...command,
|
|
29198
|
-
args: [command.args[0] * scaleFactor,
|
|
29199
|
-
// x radius
|
|
29200
|
-
command.args[1] * scaleFactor,
|
|
29201
|
-
// y radius
|
|
29202
|
-
command.args[2],
|
|
29203
|
-
// rotation
|
|
29204
|
-
command.args[3],
|
|
29205
|
-
// largeArc flag
|
|
29206
|
-
command.args[4],
|
|
29207
|
-
// sweep flag
|
|
29208
|
-
command.args[5] * scaleFactor,
|
|
29209
|
-
// end x
|
|
29210
|
-
command.args[6] * scaleFactor // end y
|
|
29211
|
-
]
|
|
29212
|
-
};
|
|
29213
|
-
|
|
29214
|
-
default:
|
|
29215
|
-
return {
|
|
29216
|
-
...command,
|
|
29217
|
-
args: command.args.map(c => c * scaleFactor)
|
|
29218
|
-
};
|
|
29219
|
-
}
|
|
29220
|
-
};
|
|
29221
|
-
}
|
|
29222
|
-
|
|
29223
28965
|
// We use the pathBuilder here to scale up the SVG path coordinates used
|
|
29224
28966
|
// elsewhere for arrowheads. We scale in the path itself instead of using a CSS
|
|
29225
28967
|
// transform because a transform would also scale up the stroke weight, and we
|
|
@@ -29249,7 +28991,10 @@ function AxisArrows() {
|
|
|
29249
28991
|
range: [[xMin, xMax], [yMin, yMax]]
|
|
29250
28992
|
} = useGraphConfig();
|
|
29251
28993
|
const axisColor = "var(--mafs-fg)";
|
|
29252
|
-
|
|
28994
|
+
|
|
28995
|
+
// Only render the arrows if the axis is within the visible range
|
|
28996
|
+
// as otherwise the arrows will be rendered outside the graph
|
|
28997
|
+
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, {
|
|
29253
28998
|
color: axisColor,
|
|
29254
28999
|
tip: [xMin, 0],
|
|
29255
29000
|
angle: 180
|
|
@@ -29257,7 +29002,7 @@ function AxisArrows() {
|
|
|
29257
29002
|
color: axisColor,
|
|
29258
29003
|
tip: [xMax, 0],
|
|
29259
29004
|
angle: 0
|
|
29260
|
-
}), /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
|
|
29005
|
+
})), !(xMin > 0 || xMax < 0) && /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
|
|
29261
29006
|
color: axisColor,
|
|
29262
29007
|
tip: [0, yMin],
|
|
29263
29008
|
angle: 90
|
|
@@ -29265,39 +29010,76 @@ function AxisArrows() {
|
|
|
29265
29010
|
color: axisColor,
|
|
29266
29011
|
tip: [0, yMax],
|
|
29267
29012
|
angle: 270
|
|
29268
|
-
}));
|
|
29013
|
+
})));
|
|
29269
29014
|
}
|
|
29270
29015
|
|
|
29016
|
+
function AxisLabels() {
|
|
29017
|
+
const {
|
|
29018
|
+
range,
|
|
29019
|
+
labels,
|
|
29020
|
+
width,
|
|
29021
|
+
height
|
|
29022
|
+
} = useGraphConfig();
|
|
29023
|
+
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
29024
|
+
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
29025
|
+
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
29026
|
+
const graphInfo = {
|
|
29027
|
+
range,
|
|
29028
|
+
width,
|
|
29029
|
+
height
|
|
29030
|
+
};
|
|
29031
|
+
const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
|
|
29032
|
+
// The default location for the y-axis-label is at the maximum y point
|
|
29033
|
+
// This is just underneath the tick line for that point. The -24 moves the
|
|
29034
|
+
// label up one grid square, so it sits on top of the graph
|
|
29035
|
+
const [x2, y2] = mafs.vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
|
|
29036
|
+
const {
|
|
29037
|
+
TeX
|
|
29038
|
+
} = getDependencies();
|
|
29039
|
+
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("span", {
|
|
29040
|
+
style: {
|
|
29041
|
+
position: "absolute",
|
|
29042
|
+
left: x1,
|
|
29043
|
+
top: y1,
|
|
29044
|
+
fontSize: "14px",
|
|
29045
|
+
transform: "translate(7px, -50%)"
|
|
29046
|
+
}
|
|
29047
|
+
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
29048
|
+
style: {
|
|
29049
|
+
position: "absolute",
|
|
29050
|
+
left: x2,
|
|
29051
|
+
top: y2,
|
|
29052
|
+
fontSize: "14px",
|
|
29053
|
+
transform: "translate(-50%, 0px)"
|
|
29054
|
+
}
|
|
29055
|
+
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, yAxisLabelText)));
|
|
29056
|
+
}
|
|
29057
|
+
|
|
29058
|
+
// The size of the ticks and labels in pixels
|
|
29271
29059
|
const tickSize = 10;
|
|
29272
|
-
const
|
|
29273
|
-
stroke: "black",
|
|
29274
|
-
strokeWidth: 1
|
|
29275
|
-
};
|
|
29060
|
+
const tickLabelSize = 14;
|
|
29276
29061
|
const YGridTick = _ref => {
|
|
29277
29062
|
let {
|
|
29278
29063
|
y,
|
|
29279
|
-
|
|
29064
|
+
range
|
|
29280
29065
|
} = _ref;
|
|
29066
|
+
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29067
|
+
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
29068
|
+
// the ticks and labels would render outside of the clipping-mask.
|
|
29281
29069
|
let xPointOnAxis = 0;
|
|
29282
|
-
|
|
29283
|
-
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
29284
|
-
// even if they are outside the graph's range.
|
|
29285
|
-
if (graphInfo.range[X][MIN] > 0) {
|
|
29070
|
+
if (range[X][MIN] > 0) {
|
|
29286
29071
|
// If the graph is on the positive side of the x-axis, lock the ticks to the left side of the graph
|
|
29287
|
-
xPointOnAxis =
|
|
29072
|
+
xPointOnAxis = range[X][MIN];
|
|
29288
29073
|
}
|
|
29289
|
-
if (
|
|
29074
|
+
if (range[X][MAX] < 0) {
|
|
29290
29075
|
// If the graph is on the negative side of the x-axis, lock the ticks to the right side of the graph
|
|
29291
|
-
xPointOnAxis =
|
|
29076
|
+
xPointOnAxis = range[X][MAX];
|
|
29292
29077
|
}
|
|
29078
|
+
|
|
29079
|
+
// Convert the Vector2 coordinates to pixel coordinates
|
|
29293
29080
|
const pointOnAxis = [xPointOnAxis, y];
|
|
29294
29081
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
29295
29082
|
|
|
29296
|
-
// If the tick is on the edge of the graph's range, don't render it
|
|
29297
|
-
if (yPosition === -graphInfo.height || yPosition === graphInfo.height + 20) {
|
|
29298
|
-
return null;
|
|
29299
|
-
}
|
|
29300
|
-
|
|
29301
29083
|
// Position of the start of the tick
|
|
29302
29084
|
const x1 = xPosition - tickSize / 2;
|
|
29303
29085
|
const y1 = yPosition;
|
|
@@ -29305,40 +29087,56 @@ const YGridTick = _ref => {
|
|
|
29305
29087
|
// Position of the end of the tick
|
|
29306
29088
|
const x2 = xPosition + tickSize / 2;
|
|
29307
29089
|
const y2 = yPosition;
|
|
29090
|
+
|
|
29091
|
+
// Adjust the y position of the x-axis labels based on
|
|
29092
|
+
// whether the x-axis is above, within, or below the graph
|
|
29093
|
+
const xAdjustment = range[X][MAX] <= 0 ? tickLabelSize * 1.5 : -tickLabelSize * 1.1;
|
|
29094
|
+
const xPositionText = xPosition + xAdjustment;
|
|
29095
|
+
const yPositionText = yPosition + tickLabelSize * 0.25; // Center the text vertically on the tick
|
|
29096
|
+
|
|
29097
|
+
// If the graph displays both the y and x axis lines within the graph, we want
|
|
29098
|
+
// to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
|
|
29099
|
+
const showLabel = shouldShowLabel(y, range);
|
|
29308
29100
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29309
|
-
className: "
|
|
29101
|
+
className: "tick"
|
|
29310
29102
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
29311
29103
|
x1: x1,
|
|
29312
29104
|
y1: y1,
|
|
29313
29105
|
x2: x2,
|
|
29314
29106
|
y2: y2,
|
|
29315
|
-
|
|
29316
|
-
})
|
|
29107
|
+
className: "axis-tick"
|
|
29108
|
+
}), showLabel && /*#__PURE__*/React__namespace.createElement("text", {
|
|
29109
|
+
className: "axis-tick-label",
|
|
29110
|
+
style: {
|
|
29111
|
+
fontSize: tickLabelSize
|
|
29112
|
+
},
|
|
29113
|
+
textAnchor: "end",
|
|
29114
|
+
x: xPositionText,
|
|
29115
|
+
y: yPositionText
|
|
29116
|
+
}, y.toString()));
|
|
29317
29117
|
};
|
|
29318
29118
|
const XGridTick = _ref2 => {
|
|
29319
29119
|
let {
|
|
29320
29120
|
x,
|
|
29321
|
-
|
|
29121
|
+
range
|
|
29322
29122
|
} = _ref2;
|
|
29123
|
+
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29124
|
+
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
29125
|
+
// the ticks and labels would render outside of the clipping-mask.
|
|
29323
29126
|
let yPointOnAxis = 0;
|
|
29324
|
-
|
|
29325
|
-
// even if they are outside the graph's range.
|
|
29326
|
-
if (graphInfo.range[Y][MIN] > 0) {
|
|
29127
|
+
if (range[Y][MIN] > 0) {
|
|
29327
29128
|
// If the graph is on the positive side of the y-axis, lock the ticks to the top of the graph
|
|
29328
|
-
yPointOnAxis =
|
|
29129
|
+
yPointOnAxis = range[Y][MIN];
|
|
29329
29130
|
}
|
|
29330
|
-
if (
|
|
29131
|
+
if (range[Y][MAX] < 0) {
|
|
29331
29132
|
// If the graph is on the negative side of the x-axis, lock the ticks to the bottom of the graph
|
|
29332
|
-
yPointOnAxis =
|
|
29133
|
+
yPointOnAxis = range[Y][MAX];
|
|
29333
29134
|
}
|
|
29135
|
+
|
|
29136
|
+
// Convert the Vector2 coordinates to pixel coordinates
|
|
29334
29137
|
const pointOnAxis = [x, yPointOnAxis];
|
|
29335
29138
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
29336
29139
|
|
|
29337
|
-
// If the tick is on the edge of the graph's range, don't render it
|
|
29338
|
-
if (xPosition === -graphInfo.width / 2 || xPosition === graphInfo.width) {
|
|
29339
|
-
return null;
|
|
29340
|
-
}
|
|
29341
|
-
|
|
29342
29140
|
// Position of the start of the tick
|
|
29343
29141
|
const x1 = xPosition;
|
|
29344
29142
|
const y1 = yPosition + tickSize / 2;
|
|
@@ -29346,21 +29144,56 @@ const XGridTick = _ref2 => {
|
|
|
29346
29144
|
// Position of the end of the tick
|
|
29347
29145
|
const x2 = xPosition;
|
|
29348
29146
|
const y2 = yPosition - tickSize / 2;
|
|
29147
|
+
|
|
29148
|
+
// Adjust the Y position of the x-axis labels based on
|
|
29149
|
+
// whether the x-axis is above, within, or below the graph
|
|
29150
|
+
const yAdjustment = range[Y][MAX] < 0 ? -tickLabelSize : tickLabelSize * 1.75;
|
|
29151
|
+
|
|
29152
|
+
// Adjust the X position of the x-axis labels based on
|
|
29153
|
+
// whether the label is positive or negative, in order to
|
|
29154
|
+
// account for the width of the negative sign
|
|
29155
|
+
const xAdjustment = x < 0 ? -2 : 0;
|
|
29156
|
+
|
|
29157
|
+
// Apply the adjustments to the x and y positions for the text
|
|
29158
|
+
const xPositionText = xPosition + xAdjustment;
|
|
29159
|
+
const yPositionText = yPosition + yAdjustment;
|
|
29349
29160
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29350
|
-
className: "
|
|
29161
|
+
className: "tick"
|
|
29351
29162
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
29352
29163
|
x1: x1,
|
|
29353
29164
|
y1: y1,
|
|
29354
29165
|
x2: x2,
|
|
29355
29166
|
y2: y2,
|
|
29356
|
-
|
|
29357
|
-
})
|
|
29167
|
+
className: "axis-tick"
|
|
29168
|
+
}), /*#__PURE__*/React__namespace.createElement("text", {
|
|
29169
|
+
className: "axis-tick-label",
|
|
29170
|
+
style: {
|
|
29171
|
+
fontSize: tickLabelSize
|
|
29172
|
+
},
|
|
29173
|
+
textAnchor: "middle",
|
|
29174
|
+
x: xPositionText,
|
|
29175
|
+
y: yPositionText
|
|
29176
|
+
}, x.toString()));
|
|
29177
|
+
};
|
|
29178
|
+
|
|
29179
|
+
// Determines whether to show the label for the given tick
|
|
29180
|
+
// Currently, the only condition is to hide the label at -1
|
|
29181
|
+
// on the y-axis when the x-axis is within the graph
|
|
29182
|
+
const shouldShowLabel = (number, range) => {
|
|
29183
|
+
let showLabel = true;
|
|
29184
|
+
|
|
29185
|
+
// If the x-axis is within the graph and the y-axis is at -1, hide the label
|
|
29186
|
+
if (range[X][MIN] < -1 && range[X][MAX] > 0 && number === -1) {
|
|
29187
|
+
showLabel = false;
|
|
29188
|
+
}
|
|
29189
|
+
return showLabel;
|
|
29358
29190
|
};
|
|
29359
29191
|
function generateTickLocations(tickStep, min, max) {
|
|
29360
29192
|
const ticks = [];
|
|
29361
29193
|
|
|
29362
29194
|
// Add ticks in the positive direction
|
|
29363
|
-
|
|
29195
|
+
const start = Math.max(min, 0);
|
|
29196
|
+
for (let i = start + tickStep; i < max; i += tickStep) {
|
|
29364
29197
|
ticks.push(i);
|
|
29365
29198
|
}
|
|
29366
29199
|
|
|
@@ -29375,34 +29208,34 @@ function generateTickLocations(tickStep, min, max) {
|
|
|
29375
29208
|
const AxisTicks = () => {
|
|
29376
29209
|
const {
|
|
29377
29210
|
tickStep,
|
|
29378
|
-
range
|
|
29379
|
-
width,
|
|
29380
|
-
height
|
|
29211
|
+
range
|
|
29381
29212
|
} = useGraphConfig();
|
|
29382
|
-
const graphInfo = {
|
|
29383
|
-
range,
|
|
29384
|
-
width,
|
|
29385
|
-
height
|
|
29386
|
-
};
|
|
29387
29213
|
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
29388
29214
|
const [xTickStep, yTickStep] = tickStep;
|
|
29215
|
+
|
|
29216
|
+
// Generate the tick locations & labels for the x and y axes
|
|
29389
29217
|
const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
|
|
29390
29218
|
const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
|
|
29391
29219
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29392
|
-
className: "axis-ticks"
|
|
29220
|
+
className: "axis-ticks",
|
|
29221
|
+
role: "presentation"
|
|
29222
|
+
}, /*#__PURE__*/React__namespace.createElement("g", {
|
|
29223
|
+
className: "y-axis-ticks"
|
|
29393
29224
|
}, yGridTicks.map(y => {
|
|
29394
29225
|
return /*#__PURE__*/React__namespace.createElement(YGridTick, {
|
|
29395
29226
|
y: y,
|
|
29396
29227
|
key: `y-grid-tick-${y}`,
|
|
29397
|
-
|
|
29228
|
+
range: range
|
|
29398
29229
|
});
|
|
29399
|
-
}),
|
|
29230
|
+
})), /*#__PURE__*/React__namespace.createElement("g", {
|
|
29231
|
+
className: "x-axis-ticks"
|
|
29232
|
+
}, xGridTicks.map(x => {
|
|
29400
29233
|
return /*#__PURE__*/React__namespace.createElement(XGridTick, {
|
|
29401
29234
|
x: x,
|
|
29402
29235
|
key: `x-grid-tick-${x}`,
|
|
29403
|
-
|
|
29236
|
+
range: range
|
|
29404
29237
|
});
|
|
29405
|
-
}));
|
|
29238
|
+
})));
|
|
29406
29239
|
};
|
|
29407
29240
|
|
|
29408
29241
|
/**
|
|
@@ -29423,12 +29256,10 @@ const axisOptions = (props, axisIndex) => {
|
|
|
29423
29256
|
};
|
|
29424
29257
|
};
|
|
29425
29258
|
const Grid = props => {
|
|
29426
|
-
return props.markings === "none" ? null : /*#__PURE__*/React__namespace.createElement(
|
|
29259
|
+
return props.markings === "none" ? null : /*#__PURE__*/React__namespace.createElement(mafs.Coordinates.Cartesian, {
|
|
29427
29260
|
xAxis: axisOptions(props, X),
|
|
29428
29261
|
yAxis: axisOptions(props, Y)
|
|
29429
|
-
})
|
|
29430
|
-
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
29431
|
-
props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)));
|
|
29262
|
+
});
|
|
29432
29263
|
};
|
|
29433
29264
|
|
|
29434
29265
|
// Displays a stylized open eye: lint warnings are visible
|
|
@@ -32186,7 +32017,8 @@ function UnlimitedPointGraph(props) {
|
|
|
32186
32017
|
const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
|
|
32187
32018
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
|
|
32188
32019
|
style: {
|
|
32189
|
-
fill: "rgba(0,0,0,0)"
|
|
32020
|
+
fill: "rgba(0,0,0,0)",
|
|
32021
|
+
cursor: "crosshair"
|
|
32190
32022
|
},
|
|
32191
32023
|
width: widthPx,
|
|
32192
32024
|
height: heightPx,
|
|
@@ -32370,6 +32202,22 @@ const MafsGraph = props => {
|
|
|
32370
32202
|
const uniqueId = React__namespace.useId();
|
|
32371
32203
|
const descriptionId = `interactive-graph-description-${uniqueId}`;
|
|
32372
32204
|
const graphRef = React__namespace.useRef(null);
|
|
32205
|
+
|
|
32206
|
+
// Set up the SVG attributes for the nested SVGs that help lock
|
|
32207
|
+
// the grid and graph elements to the bounds of the graph.
|
|
32208
|
+
const {
|
|
32209
|
+
viewboxX,
|
|
32210
|
+
viewboxY
|
|
32211
|
+
} = calculateNestedSVGCoords(state.range, width, height);
|
|
32212
|
+
const viewBox = `${viewboxX} ${viewboxY} ${width} ${height}`;
|
|
32213
|
+
const nestedSVGAttributes = {
|
|
32214
|
+
width,
|
|
32215
|
+
height,
|
|
32216
|
+
viewBox,
|
|
32217
|
+
preserveAspectRatio: "xMidYMin",
|
|
32218
|
+
x: viewboxX,
|
|
32219
|
+
y: viewboxY
|
|
32220
|
+
};
|
|
32373
32221
|
return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
|
|
32374
32222
|
value: {
|
|
32375
32223
|
range: state.range,
|
|
@@ -32391,7 +32239,7 @@ const MafsGraph = props => {
|
|
|
32391
32239
|
padding: "25px 25px 0 0",
|
|
32392
32240
|
boxSizing: "content-box",
|
|
32393
32241
|
marginLeft: "20px",
|
|
32394
|
-
marginBottom: "
|
|
32242
|
+
marginBottom: "30px",
|
|
32395
32243
|
pointerEvents: props.static ? "none" : "auto",
|
|
32396
32244
|
userSelect: "none",
|
|
32397
32245
|
width,
|
|
@@ -32426,7 +32274,7 @@ const MafsGraph = props => {
|
|
|
32426
32274
|
bottom: 0,
|
|
32427
32275
|
left: 0
|
|
32428
32276
|
}
|
|
32429
|
-
}, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null)
|
|
32277
|
+
}, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null)), /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
|
|
32430
32278
|
preserveAspectRatio: false,
|
|
32431
32279
|
viewBox: {
|
|
32432
32280
|
x: state.range[X],
|
|
@@ -32437,37 +32285,59 @@ const MafsGraph = props => {
|
|
|
32437
32285
|
zoom: false,
|
|
32438
32286
|
width: width,
|
|
32439
32287
|
height: height
|
|
32440
|
-
}, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement(Grid, {
|
|
32441
|
-
tickStep: props.step,
|
|
32288
|
+
}, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, /*#__PURE__*/React__namespace.createElement(Grid, {
|
|
32442
32289
|
gridStep: props.gridStep,
|
|
32443
32290
|
range: state.range,
|
|
32444
32291
|
containerSizeClass: props.containerSizeClass,
|
|
32445
|
-
markings: props.markings
|
|
32446
|
-
|
|
32292
|
+
markings: props.markings,
|
|
32293
|
+
width: width,
|
|
32294
|
+
height: height
|
|
32295
|
+
})),
|
|
32296
|
+
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
32297
|
+
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, {
|
|
32447
32298
|
lockedFigures: props.lockedFigures,
|
|
32448
32299
|
range: state.range
|
|
32449
32300
|
}), props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
|
|
32450
32301
|
state,
|
|
32451
32302
|
dispatch
|
|
32452
|
-
})))), renderGraphControls({
|
|
32303
|
+
}))))), renderGraphControls({
|
|
32453
32304
|
state,
|
|
32454
|
-
dispatch
|
|
32305
|
+
dispatch,
|
|
32306
|
+
width
|
|
32455
32307
|
})));
|
|
32456
32308
|
};
|
|
32457
|
-
const renderPointGraphControls = props => /*#__PURE__*/React__namespace.createElement(
|
|
32309
|
+
const renderPointGraphControls = props => /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
32310
|
+
style: {
|
|
32311
|
+
flexDirection: "row",
|
|
32312
|
+
width: props.width
|
|
32313
|
+
}
|
|
32314
|
+
}, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
|
|
32458
32315
|
kind: "secondary",
|
|
32459
32316
|
style: {
|
|
32460
32317
|
width: "100%",
|
|
32461
32318
|
marginLeft: "20px"
|
|
32462
32319
|
},
|
|
32320
|
+
tabIndex: 0,
|
|
32463
32321
|
onClick: () => {
|
|
32464
32322
|
props.dispatch(actions.pointGraph.addPoint([0, 0]));
|
|
32465
32323
|
}
|
|
32466
|
-
}, "Add Point")
|
|
32324
|
+
}, "Add Point"), (props.state.focusedPointIndex !== null || props.state.previouslyFocusedPointIndex !== null) && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
|
|
32325
|
+
kind: "secondary",
|
|
32326
|
+
color: "destructive",
|
|
32327
|
+
tabIndex: 0,
|
|
32328
|
+
style: {
|
|
32329
|
+
width: "100%",
|
|
32330
|
+
marginLeft: "20px"
|
|
32331
|
+
},
|
|
32332
|
+
onClick: event => {
|
|
32333
|
+
props.dispatch(actions.pointGraph.removePoint(props.state.previouslyFocusedPointIndex));
|
|
32334
|
+
}
|
|
32335
|
+
}, "Remove Point"));
|
|
32467
32336
|
const renderGraphControls = props => {
|
|
32468
32337
|
const {
|
|
32469
32338
|
state,
|
|
32470
|
-
dispatch
|
|
32339
|
+
dispatch,
|
|
32340
|
+
width
|
|
32471
32341
|
} = props;
|
|
32472
32342
|
const {
|
|
32473
32343
|
type
|
|
@@ -32477,7 +32347,8 @@ const renderGraphControls = props => {
|
|
|
32477
32347
|
if (state.numPoints === "unlimited") {
|
|
32478
32348
|
return renderPointGraphControls({
|
|
32479
32349
|
state,
|
|
32480
|
-
dispatch
|
|
32350
|
+
dispatch,
|
|
32351
|
+
width
|
|
32481
32352
|
});
|
|
32482
32353
|
}
|
|
32483
32354
|
return null;
|
|
@@ -32485,6 +32356,56 @@ const renderGraphControls = props => {
|
|
|
32485
32356
|
return null;
|
|
32486
32357
|
}
|
|
32487
32358
|
};
|
|
32359
|
+
|
|
32360
|
+
// Calculate the difference between the min and max values of a range
|
|
32361
|
+
const getRangeDiff = range => {
|
|
32362
|
+
const [min, max] = range;
|
|
32363
|
+
return Math.abs(max - min);
|
|
32364
|
+
};
|
|
32365
|
+
|
|
32366
|
+
// We need to adjust the nested SVG viewbox x and Y values based on the range of the graph in order
|
|
32367
|
+
// to ensure that the graph is sized and positioned correctly within the Mafs SVG and the clipping mask.
|
|
32368
|
+
// Exported for testing.
|
|
32369
|
+
const calculateNestedSVGCoords = (range, width, height) => {
|
|
32370
|
+
// X RANGE
|
|
32371
|
+
let viewboxX = 0; // When xMin is 0, we want to use 0 as the viewboxX value
|
|
32372
|
+
const totalXRange = getRangeDiff(range[X]);
|
|
32373
|
+
const gridCellWidth = width / totalXRange;
|
|
32374
|
+
const minX = range[X][MIN];
|
|
32375
|
+
|
|
32376
|
+
// If xMin is entirely positive, we need to adjust the
|
|
32377
|
+
// viewboxX to be the grid cell width multiplied by xMin
|
|
32378
|
+
if (minX > 0) {
|
|
32379
|
+
viewboxX = gridCellWidth * Math.abs(minX);
|
|
32380
|
+
}
|
|
32381
|
+
// If xMin is negative, we need to adjust the viewboxX to be
|
|
32382
|
+
// the negative value of the grid cell width multiplied by xMin
|
|
32383
|
+
if (minX < 0) {
|
|
32384
|
+
viewboxX = -gridCellWidth * Math.abs(minX);
|
|
32385
|
+
}
|
|
32386
|
+
|
|
32387
|
+
// Y RANGE
|
|
32388
|
+
let viewboxY = -height; // When yMin is 0, we want to use the negative value of the graph height
|
|
32389
|
+
const totalYRange = getRangeDiff(range[Y]);
|
|
32390
|
+
const gridCellHeight = height / totalYRange;
|
|
32391
|
+
const minY = range[Y][MIN];
|
|
32392
|
+
|
|
32393
|
+
// If the y range is entirely positive, we want a negative sum of the
|
|
32394
|
+
// height and the gridcell height multiplied by the absolute value of yMin
|
|
32395
|
+
if (minY > 0) {
|
|
32396
|
+
viewboxY = -height - gridCellHeight * Math.abs(minY);
|
|
32397
|
+
}
|
|
32398
|
+
|
|
32399
|
+
// If the yMin is negative, we want to multiply the gridcell height
|
|
32400
|
+
// by the absolute value of yMin, and subtract the full height of the graph
|
|
32401
|
+
if (minY < 0) {
|
|
32402
|
+
viewboxY = gridCellHeight * Math.abs(minY) - height;
|
|
32403
|
+
}
|
|
32404
|
+
return {
|
|
32405
|
+
viewboxX,
|
|
32406
|
+
viewboxY
|
|
32407
|
+
};
|
|
32408
|
+
};
|
|
32488
32409
|
const renderGraph = props => {
|
|
32489
32410
|
const {
|
|
32490
32411
|
state,
|
|
@@ -32601,7 +32522,8 @@ function initializeGraphState(params) {
|
|
|
32601
32522
|
type: graph.type,
|
|
32602
32523
|
coords: getPointCoords(graph, range, step),
|
|
32603
32524
|
numPoints: graph.numPoints || 0,
|
|
32604
|
-
focusedPointIndex: null
|
|
32525
|
+
focusedPointIndex: null,
|
|
32526
|
+
previouslyFocusedPointIndex: null
|
|
32605
32527
|
};
|
|
32606
32528
|
case "circle":
|
|
32607
32529
|
return {
|
|
@@ -32898,6 +32820,7 @@ function doBlurPoint(state, action) {
|
|
|
32898
32820
|
case "point":
|
|
32899
32821
|
return {
|
|
32900
32822
|
...state,
|
|
32823
|
+
previouslyFocusedPointIndex: state.focusedPointIndex,
|
|
32901
32824
|
focusedPointIndex: null
|
|
32902
32825
|
};
|
|
32903
32826
|
default:
|
|
@@ -32944,7 +32867,6 @@ function doMovePointInFigure(state, action) {
|
|
|
32944
32867
|
}
|
|
32945
32868
|
case "angle":
|
|
32946
32869
|
case "circle":
|
|
32947
|
-
throw new Error("FIXME implement circle reducer");
|
|
32948
32870
|
case "point":
|
|
32949
32871
|
case "polygon":
|
|
32950
32872
|
case "quadratic":
|
|
@@ -33270,7 +33192,8 @@ function doRemovePoint(state, action) {
|
|
|
33270
33192
|
return {
|
|
33271
33193
|
...state,
|
|
33272
33194
|
coords: state.coords.filter((_, i) => i !== action.index),
|
|
33273
|
-
focusedPointIndex: null
|
|
33195
|
+
focusedPointIndex: null,
|
|
33196
|
+
previouslyFocusedPointIndex: null
|
|
33274
33197
|
};
|
|
33275
33198
|
}
|
|
33276
33199
|
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
@@ -40094,15 +40017,15 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
40094
40017
|
|
|
40095
40018
|
// Render the text labels
|
|
40096
40019
|
results.push(graphie.style(props.isMobile ? {
|
|
40097
|
-
color: KhanColors.
|
|
40020
|
+
color: KhanColors.BLUE
|
|
40098
40021
|
} : {}, () => _label(graphie, props.labelStyle, leftLabel, leftLabel, base)));
|
|
40099
40022
|
results.push(graphie.style(props.isMobile ? {
|
|
40100
|
-
color: KhanColors.
|
|
40023
|
+
color: KhanColors.BLUE
|
|
40101
40024
|
} : {}, () => _label(graphie, props.labelStyle, rightLabel, rightLabel, base)));
|
|
40102
40025
|
|
|
40103
40026
|
// Render the labels' lines
|
|
40104
40027
|
graphie.style({
|
|
40105
|
-
stroke:
|
|
40028
|
+
stroke: KhanColors.BLUE,
|
|
40106
40029
|
strokeWidth: 3.5
|
|
40107
40030
|
}, () => {
|
|
40108
40031
|
results.push(graphie.line([leftLabel, -0.2], [leftLabel, 0.2]));
|
|
@@ -40289,21 +40212,21 @@ class NumberLine extends React__namespace.Component {
|
|
|
40289
40212
|
if (isOpen) {
|
|
40290
40213
|
fill = KhanColors._BACKGROUND;
|
|
40291
40214
|
} else if (props.static) {
|
|
40292
|
-
fill = KhanColors.
|
|
40215
|
+
fill = KhanColors.BLUE;
|
|
40293
40216
|
} else {
|
|
40294
|
-
fill = KhanColors.
|
|
40217
|
+
fill = KhanColors.GREEN;
|
|
40295
40218
|
}
|
|
40296
40219
|
const normalStyle = {
|
|
40297
40220
|
fill: fill,
|
|
40298
|
-
stroke: props.static ? KhanColors.
|
|
40221
|
+
stroke: props.static ? KhanColors.BLUE : KhanColors.GREEN,
|
|
40299
40222
|
"stroke-width": isOpen ? 3 : 1
|
|
40300
40223
|
};
|
|
40301
40224
|
const highlightStyle = {
|
|
40302
|
-
fill: isOpen ? KhanColors._BACKGROUND : KhanColors.
|
|
40225
|
+
fill: isOpen ? KhanColors._BACKGROUND : KhanColors.GREEN,
|
|
40303
40226
|
"stroke-width": isOpen ? 3 : 1
|
|
40304
40227
|
};
|
|
40305
40228
|
const mobileDotStyle = props.isInequality ? {
|
|
40306
|
-
stroke: KhanColors.
|
|
40229
|
+
stroke: KhanColors.GREEN,
|
|
40307
40230
|
"fill-opacity": isOpen ? 0 : 1
|
|
40308
40231
|
} : {};
|
|
40309
40232
|
return /*#__PURE__*/React__namespace.createElement(MovablePoint
|
|
@@ -40363,7 +40286,7 @@ class NumberLine extends React__namespace.Component {
|
|
|
40363
40286
|
const end = this._getInequalityEndpoint(props);
|
|
40364
40287
|
const style = {
|
|
40365
40288
|
arrows: "->",
|
|
40366
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.
|
|
40289
|
+
stroke: this.props.apiOptions.isMobile ? KhanColors.GREEN : KhanColors.BLUE,
|
|
40367
40290
|
strokeWidth: 3.5
|
|
40368
40291
|
};
|
|
40369
40292
|
const isGreater = ["ge", "gt"].includes(props.rel);
|
|
@@ -45099,7 +45022,7 @@ const init = function (options) {
|
|
|
45099
45022
|
|
|
45100
45023
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45101
45024
|
const libName = "@khanacademy/perseus";
|
|
45102
|
-
const libVersion = "33.
|
|
45025
|
+
const libVersion = "33.2.0";
|
|
45103
45026
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45104
45027
|
|
|
45105
45028
|
const apiVersion = {
|