@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/es/index.js
CHANGED
|
@@ -8673,10 +8673,10 @@ var Raphael$1 = (function () {
|
|
|
8673
8673
|
* provided on KhanUtil.
|
|
8674
8674
|
*/
|
|
8675
8675
|
const KhanColors = {
|
|
8676
|
-
BLUE:
|
|
8676
|
+
BLUE: color.blue,
|
|
8677
8677
|
ORANGE: "#FFA500",
|
|
8678
8678
|
PINK: "#FF00AF",
|
|
8679
|
-
GREEN:
|
|
8679
|
+
GREEN: color.green,
|
|
8680
8680
|
PURPLE: "#9D38BD",
|
|
8681
8681
|
RED: "#DF0030",
|
|
8682
8682
|
GRAY: "gray",
|
|
@@ -8752,11 +8752,9 @@ const KhanColors = {
|
|
|
8752
8752
|
// of any redesigns.
|
|
8753
8753
|
_BACKGROUND: "#FDFDFD",
|
|
8754
8754
|
// TODO(eater): Get rid of this altogether.
|
|
8755
|
-
INTERACTING:
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
// KA_GREEN
|
|
8759
|
-
DYNAMIC: "#6495ED" // BLUE
|
|
8755
|
+
INTERACTING: color.green,
|
|
8756
|
+
INTERACTIVE: color.green,
|
|
8757
|
+
DYNAMIC: color.blue
|
|
8760
8758
|
};
|
|
8761
8759
|
|
|
8762
8760
|
// DrawingTransform is responsible for transforming from "math coordinates"
|
|
@@ -10036,21 +10034,50 @@ const setLabelMargins = function setLabelMargins(span, size) {
|
|
|
10036
10034
|
});
|
|
10037
10035
|
} else {
|
|
10038
10036
|
var _$container$css, _$container$width, _$span$css;
|
|
10039
|
-
|
|
10037
|
+
// We are using jQuery to collect information and calculate a scale
|
|
10038
|
+
// since we don't have a way to pass it to this function.
|
|
10039
|
+
// We need the width of the container in order to calculate the scale to apply to the label.
|
|
10040
|
+
// Unfortunately, the DOM tree leading to the label is not consistent.
|
|
10041
|
+
// Therefore, we need to check different elements and different style properties to get the width of the container.
|
|
10042
|
+
// Some graphie labels exist within an ".svg-image" container, and others do not.
|
|
10043
|
+
// All graphie labels have a ".graphie" container.
|
|
10044
|
+
// When a label has an ".svg-image" container,
|
|
10045
|
+
// the width information in the ".graphie" element is expressed as a percentage instead of pixels,
|
|
10046
|
+
// so we can't use it.
|
|
10047
|
+
// Instead, the ".svg-image" container can be used to get the width information.
|
|
10048
|
+
// The following lines determine which container element to use for the width measurement.
|
|
10049
|
+
const $svgImage = $span.closest(".svg-image");
|
|
10050
|
+
const $graphie = $span.closest(".graphie");
|
|
10051
|
+
// If an ".svg-image" container exists in the DOM tree, then use it, otherwise use the ".graphie" container.
|
|
10052
|
+
const $container = $svgImage.length > 0 ? $svgImage : $graphie;
|
|
10053
|
+
|
|
10040
10054
|
// Ensuring that the line-height of the text doesn't throw off placement of the text element.
|
|
10041
10055
|
// Inherited line-height values can really mess up placement.
|
|
10042
10056
|
$container.css("line-height", "normal");
|
|
10043
10057
|
|
|
10044
|
-
// The
|
|
10045
|
-
//
|
|
10046
|
-
const
|
|
10058
|
+
// The expected width of the graphie is found in the "max-width" property on ".svg-image" containers,
|
|
10059
|
+
// and is found in the "width" property on ".graphie" containers.
|
|
10060
|
+
const widthValues = (_$container$css = $container.css(["max-width", "width"])) != null ? _$container$css : {
|
|
10061
|
+
// The browser will always return a value, but tests won't, so ensuring something is returned.
|
|
10062
|
+
"max-width": "0px"
|
|
10063
|
+
};
|
|
10064
|
+
const expectedWidth =
|
|
10065
|
+
// Verified in Chrome, Firefox, and Safari - all return the same "none" value if the property does not exist.
|
|
10066
|
+
widthValues["max-width"] !== "none" ? widthValues["max-width"] : widthValues["width"];
|
|
10047
10067
|
// We can calculate the true scale of the graphie by taking actual width and dividing by the expected width.
|
|
10048
|
-
// NOTE: Using '
|
|
10049
|
-
|
|
10068
|
+
// NOTE: Using 'replace' to remove the "px" unit from the end of the expected width value.
|
|
10069
|
+
let scale = ((_$container$width = $container.width()) != null ? _$container$width : 0) / parseInt(expectedWidth.replace(/px$/, "")) * 100;
|
|
10070
|
+
// If something failed in the calculation, then default to 100% scale.
|
|
10071
|
+
if (isNaN(scale)) {
|
|
10072
|
+
scale = 100;
|
|
10073
|
+
} else if (scale === 0) {
|
|
10074
|
+
scale = 100;
|
|
10075
|
+
}
|
|
10050
10076
|
|
|
10051
10077
|
// Any padding needs to be scaled accordingly.
|
|
10052
|
-
const
|
|
10053
|
-
const
|
|
10078
|
+
const padding = (_$span$css = $span.css("padding")) != null ? _$span$css : "0px";
|
|
10079
|
+
const currentPadding = padding !== "none" ? padding : "0px";
|
|
10080
|
+
const newPadding = Math.round(parseInt(currentPadding.replace(/px$/, "")) * scale) / 100;
|
|
10054
10081
|
|
|
10055
10082
|
// "multipliers" basically move the position of the text by using 1, 0, -1
|
|
10056
10083
|
// 'margin-left' and 'margin-top' are used to position the text.
|
|
@@ -28320,6 +28347,94 @@ Graph.defaultProps = {
|
|
|
28320
28347
|
isMobile: false
|
|
28321
28348
|
};
|
|
28322
28349
|
|
|
28350
|
+
function pathBuilder() {
|
|
28351
|
+
return new PathBuilder();
|
|
28352
|
+
}
|
|
28353
|
+
class PathBuilder {
|
|
28354
|
+
constructor() {
|
|
28355
|
+
this.path = [];
|
|
28356
|
+
this.scaleFactor = 1;
|
|
28357
|
+
}
|
|
28358
|
+
build() {
|
|
28359
|
+
return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
|
|
28360
|
+
}
|
|
28361
|
+
|
|
28362
|
+
// add a move (M) command to the path
|
|
28363
|
+
move(x, y) {
|
|
28364
|
+
this.path.push({
|
|
28365
|
+
action: "M",
|
|
28366
|
+
args: [x, y]
|
|
28367
|
+
});
|
|
28368
|
+
return this;
|
|
28369
|
+
}
|
|
28370
|
+
line(x, y) {
|
|
28371
|
+
this.path.push({
|
|
28372
|
+
action: "L",
|
|
28373
|
+
args: [x, y]
|
|
28374
|
+
});
|
|
28375
|
+
return this;
|
|
28376
|
+
}
|
|
28377
|
+
circularArc(radius, toX, toY, {
|
|
28378
|
+
sweep = false,
|
|
28379
|
+
largeArc = false
|
|
28380
|
+
} = {}) {
|
|
28381
|
+
this.path.push({
|
|
28382
|
+
action: "A",
|
|
28383
|
+
args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
|
|
28384
|
+
});
|
|
28385
|
+
return this;
|
|
28386
|
+
}
|
|
28387
|
+
|
|
28388
|
+
// add a curve (C) command to the path
|
|
28389
|
+
curve(control1X, control1Y, control2X, control2Y, endX, endY) {
|
|
28390
|
+
this.path.push({
|
|
28391
|
+
action: "C",
|
|
28392
|
+
args: [control1X, control1Y, control2X, control2Y, endX, endY]
|
|
28393
|
+
});
|
|
28394
|
+
return this;
|
|
28395
|
+
}
|
|
28396
|
+
|
|
28397
|
+
// Scale all coordinates of the path by a factor
|
|
28398
|
+
scale(factor) {
|
|
28399
|
+
this.scaleFactor *= factor;
|
|
28400
|
+
return this;
|
|
28401
|
+
}
|
|
28402
|
+
}
|
|
28403
|
+
function commandToString(command) {
|
|
28404
|
+
return `${command.action}${command.args.join(" ")}`;
|
|
28405
|
+
}
|
|
28406
|
+
function scaleCommandBy(scaleFactor) {
|
|
28407
|
+
return command => {
|
|
28408
|
+
switch (command.action) {
|
|
28409
|
+
case "A":
|
|
28410
|
+
// TODO(benchristel): do we want to refactor this switch to
|
|
28411
|
+
// use polymorphism?
|
|
28412
|
+
// Arc command
|
|
28413
|
+
return _extends({}, command, {
|
|
28414
|
+
args: [command.args[0] * scaleFactor,
|
|
28415
|
+
// x radius
|
|
28416
|
+
command.args[1] * scaleFactor,
|
|
28417
|
+
// y radius
|
|
28418
|
+
command.args[2],
|
|
28419
|
+
// rotation
|
|
28420
|
+
command.args[3],
|
|
28421
|
+
// largeArc flag
|
|
28422
|
+
command.args[4],
|
|
28423
|
+
// sweep flag
|
|
28424
|
+
command.args[5] * scaleFactor,
|
|
28425
|
+
// end x
|
|
28426
|
+
command.args[6] * scaleFactor // end y
|
|
28427
|
+
]
|
|
28428
|
+
});
|
|
28429
|
+
|
|
28430
|
+
default:
|
|
28431
|
+
return _extends({}, command, {
|
|
28432
|
+
args: command.args.map(c => c * scaleFactor)
|
|
28433
|
+
});
|
|
28434
|
+
}
|
|
28435
|
+
};
|
|
28436
|
+
}
|
|
28437
|
+
|
|
28323
28438
|
// Restricts `value` to be between `min` and `max`.
|
|
28324
28439
|
function clamp(value, min, max) {
|
|
28325
28440
|
if (value < min) {
|
|
@@ -28536,377 +28651,6 @@ function pixelsToVectors(pixels, graphState) {
|
|
|
28536
28651
|
});
|
|
28537
28652
|
}
|
|
28538
28653
|
|
|
28539
|
-
function AxisLabels() {
|
|
28540
|
-
const {
|
|
28541
|
-
range,
|
|
28542
|
-
labels,
|
|
28543
|
-
width,
|
|
28544
|
-
height
|
|
28545
|
-
} = useGraphConfig();
|
|
28546
|
-
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28547
|
-
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28548
|
-
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
28549
|
-
const graphInfo = {
|
|
28550
|
-
range,
|
|
28551
|
-
width,
|
|
28552
|
-
height
|
|
28553
|
-
};
|
|
28554
|
-
const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
|
|
28555
|
-
// The default location for the y-axis-label is at the maximum y point
|
|
28556
|
-
// This is just underneath the tick line for that point. The -24 moves the
|
|
28557
|
-
// label up one grid square, so it sits on top of the graph
|
|
28558
|
-
const [x2, y2] = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
|
|
28559
|
-
const {
|
|
28560
|
-
TeX
|
|
28561
|
-
} = getDependencies();
|
|
28562
|
-
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
|
|
28563
|
-
style: {
|
|
28564
|
-
position: "absolute",
|
|
28565
|
-
left: x1,
|
|
28566
|
-
top: y1,
|
|
28567
|
-
fontSize: "14px",
|
|
28568
|
-
transform: "translate(7px, -50%)"
|
|
28569
|
-
}
|
|
28570
|
-
}, /*#__PURE__*/React__default.createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default.createElement("span", {
|
|
28571
|
-
style: {
|
|
28572
|
-
position: "absolute",
|
|
28573
|
-
left: x2,
|
|
28574
|
-
top: y2,
|
|
28575
|
-
fontSize: "14px",
|
|
28576
|
-
transform: "translate(-50%, 0px)"
|
|
28577
|
-
}
|
|
28578
|
-
}, /*#__PURE__*/React__default.createElement(TeX, null, yAxisLabelText)));
|
|
28579
|
-
}
|
|
28580
|
-
|
|
28581
|
-
// Universal padding for the axis ticks to ensure that the labels
|
|
28582
|
-
// have enough space to render without overlapping the tick lines
|
|
28583
|
-
const tickPadding = 5;
|
|
28584
|
-
const AxisTickLabels = () => {
|
|
28585
|
-
const graphConfig = useGraphConfig();
|
|
28586
|
-
const {
|
|
28587
|
-
tickStep,
|
|
28588
|
-
range
|
|
28589
|
-
} = graphConfig;
|
|
28590
|
-
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
28591
|
-
const [xTickStep, yTickStep] = tickStep;
|
|
28592
|
-
const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
|
|
28593
|
-
const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
|
|
28594
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
28595
|
-
className: "axis-tick-labels"
|
|
28596
|
-
}, /*#__PURE__*/React.createElement(YGridAxis, {
|
|
28597
|
-
axisTicks: yGridTicks,
|
|
28598
|
-
graphConfig: graphConfig
|
|
28599
|
-
}), /*#__PURE__*/React.createElement(XGridAxis, {
|
|
28600
|
-
axisTicks: xGridTicks,
|
|
28601
|
-
graphConfig: graphConfig
|
|
28602
|
-
}));
|
|
28603
|
-
};
|
|
28604
|
-
|
|
28605
|
-
// Generate the necessary styles and ticks for the y-axis tick labels container
|
|
28606
|
-
const YGridAxis = props => {
|
|
28607
|
-
const {
|
|
28608
|
-
axisTicks,
|
|
28609
|
-
graphConfig
|
|
28610
|
-
} = props;
|
|
28611
|
-
const {
|
|
28612
|
-
width,
|
|
28613
|
-
height
|
|
28614
|
-
} = graphConfig;
|
|
28615
|
-
|
|
28616
|
-
// First get the label height by calculating the difference between the first two y-axis labels
|
|
28617
|
-
const yTopPoint = pointToPixel([0, axisTicks[0]], graphConfig);
|
|
28618
|
-
const ySecondPoint = pointToPixel([0, axisTicks[1]], graphConfig);
|
|
28619
|
-
const yLabelHeight = ySecondPoint[1] - yTopPoint[1];
|
|
28620
|
-
|
|
28621
|
-
// When rendering purely positive y-axis labels, we want to adjust the left position
|
|
28622
|
-
// from the lowest calculated tick, as yMin may be negative
|
|
28623
|
-
const yLowestTick = axisTicks[axisTicks.length - 1];
|
|
28624
|
-
const yLeftAdjustment = Math.min(Math.max(yTopPoint[0] - tickPadding, 0), width);
|
|
28625
|
-
|
|
28626
|
-
// We only want to render the 0 label on the y-axis if it is left of the grid
|
|
28627
|
-
const yAxisIsLeftOfGrid = yLeftAdjustment === 0;
|
|
28628
|
-
|
|
28629
|
-
// If the y-axis is right of the graph's viewbox/range,
|
|
28630
|
-
// we want to adjust both the positioning and text alignment
|
|
28631
|
-
const yAxisRightOfGrid = yLeftAdjustment === width;
|
|
28632
|
-
|
|
28633
|
-
// Determine the default spacing for the labels based on whether the y-axis is right of the grid
|
|
28634
|
-
const defaultSpacing = yAxisRightOfGrid ? 0.5 : 0;
|
|
28635
|
-
|
|
28636
|
-
// We also want to adjust the spacing if there are no negative y-axis labels as the extra space is not needed
|
|
28637
|
-
const marginSpacing = yLowestTick >= 0 ? defaultSpacing * 2 : defaultSpacing;
|
|
28638
|
-
|
|
28639
|
-
// Determine the text alignment based on whether the y-axis is right of the grid
|
|
28640
|
-
// We need to cast this as a const as it is used in the inline styles
|
|
28641
|
-
const textAlign = yAxisRightOfGrid ? "left" : "right";
|
|
28642
|
-
const yAxisStyles = {
|
|
28643
|
-
left: `calc(${yLeftAdjustment}px - ${marginSpacing}em)`,
|
|
28644
|
-
top: `calc(${yTopPoint[1]}px - 0.5em)`,
|
|
28645
|
-
height,
|
|
28646
|
-
textAlign,
|
|
28647
|
-
"--y-axis-label-height": yLabelHeight + "px"
|
|
28648
|
-
};
|
|
28649
|
-
const classNames = yAxisRightOfGrid ? "y-axis-tick-labels y-axis-right-of-grid" : "y-axis-tick-labels ";
|
|
28650
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
28651
|
-
className: classNames,
|
|
28652
|
-
"data-testid": "y-axis-tick-labels",
|
|
28653
|
-
style: yAxisStyles
|
|
28654
|
-
}, axisTicks.map(label => {
|
|
28655
|
-
return /*#__PURE__*/React.createElement(AxisTickLabel, {
|
|
28656
|
-
label: label,
|
|
28657
|
-
axis: "y",
|
|
28658
|
-
key: `y-grid-tick-${label}`,
|
|
28659
|
-
graphConfig: graphConfig,
|
|
28660
|
-
axisOutOfBounds: yAxisIsLeftOfGrid
|
|
28661
|
-
});
|
|
28662
|
-
}));
|
|
28663
|
-
};
|
|
28664
|
-
|
|
28665
|
-
// Generate the necessary styles and ticks for the x-axis tick labels container
|
|
28666
|
-
const XGridAxis = props => {
|
|
28667
|
-
const {
|
|
28668
|
-
axisTicks,
|
|
28669
|
-
graphConfig
|
|
28670
|
-
} = props;
|
|
28671
|
-
const {
|
|
28672
|
-
width,
|
|
28673
|
-
height
|
|
28674
|
-
} = graphConfig;
|
|
28675
|
-
|
|
28676
|
-
// Determine label width by calculating the difference between the first two x-axis labels
|
|
28677
|
-
const xFirstPoint = pointToPixel([axisTicks[0], 0], graphConfig);
|
|
28678
|
-
const xSecondPoint = pointToPixel([axisTicks[1], 0], graphConfig);
|
|
28679
|
-
const xLabelWidth = Math.abs(xSecondPoint[0] - xFirstPoint[0]);
|
|
28680
|
-
|
|
28681
|
-
// Determine the top position of the x-axis labels based on
|
|
28682
|
-
// whether the x-axis is above, within, or below the graph
|
|
28683
|
-
const xAxisAboveGraph = xFirstPoint[1] < 0;
|
|
28684
|
-
const xTopAdjustment = xAxisAboveGraph ? 0 : Math.min(xFirstPoint[1] + tickPadding, height);
|
|
28685
|
-
|
|
28686
|
-
// Determine whether the x-axis is outside of the graph's viewbox/range
|
|
28687
|
-
// This is used to conditionally render the 0 label on the x-axis
|
|
28688
|
-
const xAxisOutOfBounds = xTopAdjustment === height || xAxisAboveGraph;
|
|
28689
|
-
|
|
28690
|
-
// Determine the available space to the right of the x-axis labels
|
|
28691
|
-
const availableSpaceRight = width - xFirstPoint[0];
|
|
28692
|
-
|
|
28693
|
-
// Calculate the total combined width of ALL the x-axis labels
|
|
28694
|
-
const axisTicksWidth = axisTicks.length * xLabelWidth;
|
|
28695
|
-
|
|
28696
|
-
// Determine if we need to increase the width of the axis past the graph width
|
|
28697
|
-
const calculatedWidth = axisTicksWidth > width ? axisTicksWidth : width;
|
|
28698
|
-
|
|
28699
|
-
// Determine the right adjustment for the x-axis labels based on the available space
|
|
28700
|
-
// We're adjusting the right as the flex-direction is row-reverse
|
|
28701
|
-
const xRightAdjustment = availableSpaceRight - xLabelWidth / 2;
|
|
28702
|
-
const xAxisStyles = {
|
|
28703
|
-
top: xTopAdjustment,
|
|
28704
|
-
right: xRightAdjustment,
|
|
28705
|
-
width: calculatedWidth,
|
|
28706
|
-
"--x-axis-label-width": xLabelWidth + "px"
|
|
28707
|
-
};
|
|
28708
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
28709
|
-
className: `x-axis-tick-labels ${xAxisAboveGraph && "x-axis-top-of-grid"}`,
|
|
28710
|
-
"data-testid": "x-axis-tick-labels",
|
|
28711
|
-
style: xAxisStyles
|
|
28712
|
-
}, axisTicks.map(label => {
|
|
28713
|
-
return /*#__PURE__*/React.createElement(AxisTickLabel, {
|
|
28714
|
-
axis: "x",
|
|
28715
|
-
label: label,
|
|
28716
|
-
key: `x-grid-tick-${label}`,
|
|
28717
|
-
graphConfig: graphConfig,
|
|
28718
|
-
axisOutOfBounds: xAxisOutOfBounds
|
|
28719
|
-
});
|
|
28720
|
-
}));
|
|
28721
|
-
};
|
|
28722
|
-
|
|
28723
|
-
// Generate the individual tick label elements
|
|
28724
|
-
const AxisTickLabel = ({
|
|
28725
|
-
label,
|
|
28726
|
-
graphConfig,
|
|
28727
|
-
axisOutOfBounds,
|
|
28728
|
-
axis
|
|
28729
|
-
}) => {
|
|
28730
|
-
const {
|
|
28731
|
-
gridStep,
|
|
28732
|
-
tickStep
|
|
28733
|
-
} = graphConfig;
|
|
28734
|
-
// Determine the point on the axis based on the axis type
|
|
28735
|
-
const pointOnAxis = axis === "x" ? [label, 0] : [0, label];
|
|
28736
|
-
const pixelPoint = pointToPixel(pointOnAxis, graphConfig);
|
|
28737
|
-
|
|
28738
|
-
// Determine the relevant edge of the graph based on the axis type
|
|
28739
|
-
const graphEdge = axis === "x" ? graphConfig.width : graphConfig.height;
|
|
28740
|
-
|
|
28741
|
-
// Determine the vector index based on the axis type
|
|
28742
|
-
const vectorIndex = axis === "x" ? 0 : 1;
|
|
28743
|
-
|
|
28744
|
-
// Determine whether or not to render the tick label text
|
|
28745
|
-
const shouldShowLabel = showTickLabel({
|
|
28746
|
-
label,
|
|
28747
|
-
axisOutOfBounds,
|
|
28748
|
-
graphEdge,
|
|
28749
|
-
position: pixelPoint[vectorIndex],
|
|
28750
|
-
gridStep: gridStep[vectorIndex],
|
|
28751
|
-
tickStep: tickStep[vectorIndex]
|
|
28752
|
-
});
|
|
28753
|
-
return /*#__PURE__*/React.createElement("span", {
|
|
28754
|
-
className: `${axis}-axis-tick-label`
|
|
28755
|
-
}, shouldShowLabel && label);
|
|
28756
|
-
};
|
|
28757
|
-
|
|
28758
|
-
// Determine whether or not to show the tick label text
|
|
28759
|
-
const showTickLabel = ({
|
|
28760
|
-
gridStep,
|
|
28761
|
-
tickStep,
|
|
28762
|
-
label,
|
|
28763
|
-
graphEdge,
|
|
28764
|
-
position,
|
|
28765
|
-
axisOutOfBounds
|
|
28766
|
-
}) => {
|
|
28767
|
-
let showLabel = true;
|
|
28768
|
-
|
|
28769
|
-
// We only want to show the initial negative tick labels (e.g. -1) on each
|
|
28770
|
-
// axis if the tickStep > gridStep, to ensure that these labels do not overlap.
|
|
28771
|
-
// e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
|
|
28772
|
-
// which allows enough room for both these tick labels to render.
|
|
28773
|
-
showLabel = tickStep > gridStep ? true : label !== -tickStep;
|
|
28774
|
-
|
|
28775
|
-
// If the tick label position is greater than the relevant edge of the graph, don't render it
|
|
28776
|
-
if (position <= -graphEdge || position >= graphEdge) {
|
|
28777
|
-
showLabel = false;
|
|
28778
|
-
}
|
|
28779
|
-
|
|
28780
|
-
// If the label is 0, we only want to show it if the axis is to the left of the graph's viewbox/range
|
|
28781
|
-
// otherwise it will overlap with graph axis labels
|
|
28782
|
-
if (label === 0 && !axisOutOfBounds) {
|
|
28783
|
-
showLabel = false;
|
|
28784
|
-
}
|
|
28785
|
-
return showLabel;
|
|
28786
|
-
};
|
|
28787
|
-
|
|
28788
|
-
// Generate the tick labels for an axis based on the tick step
|
|
28789
|
-
function generateTickLocations$1(tickStep, min, max) {
|
|
28790
|
-
const positiveTicks = [];
|
|
28791
|
-
const negativeTicks = [];
|
|
28792
|
-
const ticks = [];
|
|
28793
|
-
|
|
28794
|
-
// Add 0 if it is applicable.
|
|
28795
|
-
// This will be conditionally rendered using showTickLabel.
|
|
28796
|
-
if (min <= 0 && max >= 0) {
|
|
28797
|
-
positiveTicks.push(0);
|
|
28798
|
-
}
|
|
28799
|
-
|
|
28800
|
-
// Add ticks on the positive axis
|
|
28801
|
-
// Start at the first tick after 0 or the minimum value,
|
|
28802
|
-
// taking into account that the graph may be zoomed in on the positive axis
|
|
28803
|
-
let i = Math.max(min, 0) + tickStep;
|
|
28804
|
-
for (i; min < i && i < max; i += tickStep) {
|
|
28805
|
-
positiveTicks.push(i);
|
|
28806
|
-
}
|
|
28807
|
-
|
|
28808
|
-
// Add ticks on the negative axis
|
|
28809
|
-
// Start at the first tick before 0 or the maximum value,
|
|
28810
|
-
// taking into account that the graph may be zoomed in on the negative axis
|
|
28811
|
-
i = Math.min(max, 0) - tickStep;
|
|
28812
|
-
for (i; i > min; i -= tickStep) {
|
|
28813
|
-
negativeTicks.push(i);
|
|
28814
|
-
}
|
|
28815
|
-
|
|
28816
|
-
// Reverse the ticks so that they are in the correct render order
|
|
28817
|
-
positiveTicks.reverse();
|
|
28818
|
-
ticks.push(...positiveTicks, ...negativeTicks);
|
|
28819
|
-
return ticks;
|
|
28820
|
-
}
|
|
28821
|
-
|
|
28822
|
-
function pathBuilder() {
|
|
28823
|
-
return new PathBuilder();
|
|
28824
|
-
}
|
|
28825
|
-
class PathBuilder {
|
|
28826
|
-
constructor() {
|
|
28827
|
-
this.path = [];
|
|
28828
|
-
this.scaleFactor = 1;
|
|
28829
|
-
}
|
|
28830
|
-
build() {
|
|
28831
|
-
return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("");
|
|
28832
|
-
}
|
|
28833
|
-
|
|
28834
|
-
// add a move (M) command to the path
|
|
28835
|
-
move(x, y) {
|
|
28836
|
-
this.path.push({
|
|
28837
|
-
action: "M",
|
|
28838
|
-
args: [x, y]
|
|
28839
|
-
});
|
|
28840
|
-
return this;
|
|
28841
|
-
}
|
|
28842
|
-
line(x, y) {
|
|
28843
|
-
this.path.push({
|
|
28844
|
-
action: "L",
|
|
28845
|
-
args: [x, y]
|
|
28846
|
-
});
|
|
28847
|
-
return this;
|
|
28848
|
-
}
|
|
28849
|
-
circularArc(radius, toX, toY, {
|
|
28850
|
-
sweep = false,
|
|
28851
|
-
largeArc = false
|
|
28852
|
-
} = {}) {
|
|
28853
|
-
this.path.push({
|
|
28854
|
-
action: "A",
|
|
28855
|
-
args: [radius, radius, 0 /* rotation */, largeArc ? 1 : 0, sweep ? 1 : 0, toX, toY]
|
|
28856
|
-
});
|
|
28857
|
-
return this;
|
|
28858
|
-
}
|
|
28859
|
-
|
|
28860
|
-
// add a curve (C) command to the path
|
|
28861
|
-
curve(control1X, control1Y, control2X, control2Y, endX, endY) {
|
|
28862
|
-
this.path.push({
|
|
28863
|
-
action: "C",
|
|
28864
|
-
args: [control1X, control1Y, control2X, control2Y, endX, endY]
|
|
28865
|
-
});
|
|
28866
|
-
return this;
|
|
28867
|
-
}
|
|
28868
|
-
|
|
28869
|
-
// Scale all coordinates of the path by a factor
|
|
28870
|
-
scale(factor) {
|
|
28871
|
-
this.scaleFactor *= factor;
|
|
28872
|
-
return this;
|
|
28873
|
-
}
|
|
28874
|
-
}
|
|
28875
|
-
function commandToString(command) {
|
|
28876
|
-
return `${command.action}${command.args.join(" ")}`;
|
|
28877
|
-
}
|
|
28878
|
-
function scaleCommandBy(scaleFactor) {
|
|
28879
|
-
return command => {
|
|
28880
|
-
switch (command.action) {
|
|
28881
|
-
case "A":
|
|
28882
|
-
// TODO(benchristel): do we want to refactor this switch to
|
|
28883
|
-
// use polymorphism?
|
|
28884
|
-
// Arc command
|
|
28885
|
-
return _extends({}, command, {
|
|
28886
|
-
args: [command.args[0] * scaleFactor,
|
|
28887
|
-
// x radius
|
|
28888
|
-
command.args[1] * scaleFactor,
|
|
28889
|
-
// y radius
|
|
28890
|
-
command.args[2],
|
|
28891
|
-
// rotation
|
|
28892
|
-
command.args[3],
|
|
28893
|
-
// largeArc flag
|
|
28894
|
-
command.args[4],
|
|
28895
|
-
// sweep flag
|
|
28896
|
-
command.args[5] * scaleFactor,
|
|
28897
|
-
// end x
|
|
28898
|
-
command.args[6] * scaleFactor // end y
|
|
28899
|
-
]
|
|
28900
|
-
});
|
|
28901
|
-
|
|
28902
|
-
default:
|
|
28903
|
-
return _extends({}, command, {
|
|
28904
|
-
args: command.args.map(c => c * scaleFactor)
|
|
28905
|
-
});
|
|
28906
|
-
}
|
|
28907
|
-
};
|
|
28908
|
-
}
|
|
28909
|
-
|
|
28910
28654
|
// We use the pathBuilder here to scale up the SVG path coordinates used
|
|
28911
28655
|
// elsewhere for arrowheads. We scale in the path itself instead of using a CSS
|
|
28912
28656
|
// transform because a transform would also scale up the stroke weight, and we
|
|
@@ -28937,7 +28681,10 @@ function AxisArrows() {
|
|
|
28937
28681
|
range: [[xMin, xMax], [yMin, yMax]]
|
|
28938
28682
|
} = useGraphConfig();
|
|
28939
28683
|
const axisColor = "var(--mafs-fg)";
|
|
28940
|
-
|
|
28684
|
+
|
|
28685
|
+
// Only render the arrows if the axis is within the visible range
|
|
28686
|
+
// as otherwise the arrows will be rendered outside the graph
|
|
28687
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, !(yMin > 0 || yMax < 0) && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrowhead, {
|
|
28941
28688
|
color: axisColor,
|
|
28942
28689
|
tip: [xMin, 0],
|
|
28943
28690
|
angle: 180
|
|
@@ -28945,7 +28692,7 @@ function AxisArrows() {
|
|
|
28945
28692
|
color: axisColor,
|
|
28946
28693
|
tip: [xMax, 0],
|
|
28947
28694
|
angle: 0
|
|
28948
|
-
}), /*#__PURE__*/React__default.createElement(Arrowhead, {
|
|
28695
|
+
})), !(xMin > 0 || xMax < 0) && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrowhead, {
|
|
28949
28696
|
color: axisColor,
|
|
28950
28697
|
tip: [0, yMin],
|
|
28951
28698
|
angle: 90
|
|
@@ -28953,38 +28700,75 @@ function AxisArrows() {
|
|
|
28953
28700
|
color: axisColor,
|
|
28954
28701
|
tip: [0, yMax],
|
|
28955
28702
|
angle: 270
|
|
28956
|
-
}));
|
|
28703
|
+
})));
|
|
28957
28704
|
}
|
|
28958
28705
|
|
|
28706
|
+
function AxisLabels() {
|
|
28707
|
+
const {
|
|
28708
|
+
range,
|
|
28709
|
+
labels,
|
|
28710
|
+
width,
|
|
28711
|
+
height
|
|
28712
|
+
} = useGraphConfig();
|
|
28713
|
+
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28714
|
+
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28715
|
+
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
28716
|
+
const graphInfo = {
|
|
28717
|
+
range,
|
|
28718
|
+
width,
|
|
28719
|
+
height
|
|
28720
|
+
};
|
|
28721
|
+
const [x1, y1] = pointToPixel(xAxisLabelLocation, graphInfo);
|
|
28722
|
+
// The default location for the y-axis-label is at the maximum y point
|
|
28723
|
+
// This is just underneath the tick line for that point. The -24 moves the
|
|
28724
|
+
// label up one grid square, so it sits on top of the graph
|
|
28725
|
+
const [x2, y2] = vec.add(pointToPixel(yAxisLabelLocation, graphInfo), [0, -24]);
|
|
28726
|
+
const {
|
|
28727
|
+
TeX
|
|
28728
|
+
} = getDependencies();
|
|
28729
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
|
|
28730
|
+
style: {
|
|
28731
|
+
position: "absolute",
|
|
28732
|
+
left: x1,
|
|
28733
|
+
top: y1,
|
|
28734
|
+
fontSize: "14px",
|
|
28735
|
+
transform: "translate(7px, -50%)"
|
|
28736
|
+
}
|
|
28737
|
+
}, /*#__PURE__*/React__default.createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default.createElement("span", {
|
|
28738
|
+
style: {
|
|
28739
|
+
position: "absolute",
|
|
28740
|
+
left: x2,
|
|
28741
|
+
top: y2,
|
|
28742
|
+
fontSize: "14px",
|
|
28743
|
+
transform: "translate(-50%, 0px)"
|
|
28744
|
+
}
|
|
28745
|
+
}, /*#__PURE__*/React__default.createElement(TeX, null, yAxisLabelText)));
|
|
28746
|
+
}
|
|
28747
|
+
|
|
28748
|
+
// The size of the ticks and labels in pixels
|
|
28959
28749
|
const tickSize = 10;
|
|
28960
|
-
const
|
|
28961
|
-
stroke: "black",
|
|
28962
|
-
strokeWidth: 1
|
|
28963
|
-
};
|
|
28750
|
+
const tickLabelSize = 14;
|
|
28964
28751
|
const YGridTick = ({
|
|
28965
28752
|
y,
|
|
28966
|
-
|
|
28753
|
+
range
|
|
28967
28754
|
}) => {
|
|
28755
|
+
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
28756
|
+
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
28757
|
+
// the ticks and labels would render outside of the clipping-mask.
|
|
28968
28758
|
let xPointOnAxis = 0;
|
|
28969
|
-
|
|
28970
|
-
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
28971
|
-
// even if they are outside the graph's range.
|
|
28972
|
-
if (graphInfo.range[X][MIN] > 0) {
|
|
28759
|
+
if (range[X][MIN] > 0) {
|
|
28973
28760
|
// If the graph is on the positive side of the x-axis, lock the ticks to the left side of the graph
|
|
28974
|
-
xPointOnAxis =
|
|
28761
|
+
xPointOnAxis = range[X][MIN];
|
|
28975
28762
|
}
|
|
28976
|
-
if (
|
|
28763
|
+
if (range[X][MAX] < 0) {
|
|
28977
28764
|
// If the graph is on the negative side of the x-axis, lock the ticks to the right side of the graph
|
|
28978
|
-
xPointOnAxis =
|
|
28765
|
+
xPointOnAxis = range[X][MAX];
|
|
28979
28766
|
}
|
|
28767
|
+
|
|
28768
|
+
// Convert the Vector2 coordinates to pixel coordinates
|
|
28980
28769
|
const pointOnAxis = [xPointOnAxis, y];
|
|
28981
28770
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
28982
28771
|
|
|
28983
|
-
// If the tick is on the edge of the graph's range, don't render it
|
|
28984
|
-
if (yPosition === -graphInfo.height || yPosition === graphInfo.height + 20) {
|
|
28985
|
-
return null;
|
|
28986
|
-
}
|
|
28987
|
-
|
|
28988
28772
|
// Position of the start of the tick
|
|
28989
28773
|
const x1 = xPosition - tickSize / 2;
|
|
28990
28774
|
const y1 = yPosition;
|
|
@@ -28992,39 +28776,55 @@ const YGridTick = ({
|
|
|
28992
28776
|
// Position of the end of the tick
|
|
28993
28777
|
const x2 = xPosition + tickSize / 2;
|
|
28994
28778
|
const y2 = yPosition;
|
|
28779
|
+
|
|
28780
|
+
// Adjust the y position of the x-axis labels based on
|
|
28781
|
+
// whether the x-axis is above, within, or below the graph
|
|
28782
|
+
const xAdjustment = range[X][MAX] <= 0 ? tickLabelSize * 1.5 : -tickLabelSize * 1.1;
|
|
28783
|
+
const xPositionText = xPosition + xAdjustment;
|
|
28784
|
+
const yPositionText = yPosition + tickLabelSize * 0.25; // Center the text vertically on the tick
|
|
28785
|
+
|
|
28786
|
+
// If the graph displays both the y and x axis lines within the graph, we want
|
|
28787
|
+
// to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
|
|
28788
|
+
const showLabel = shouldShowLabel(y, range);
|
|
28995
28789
|
return /*#__PURE__*/React.createElement("g", {
|
|
28996
|
-
className: "
|
|
28790
|
+
className: "tick"
|
|
28997
28791
|
}, /*#__PURE__*/React.createElement("line", {
|
|
28998
28792
|
x1: x1,
|
|
28999
28793
|
y1: y1,
|
|
29000
28794
|
x2: x2,
|
|
29001
28795
|
y2: y2,
|
|
29002
|
-
|
|
29003
|
-
})
|
|
28796
|
+
className: "axis-tick"
|
|
28797
|
+
}), showLabel && /*#__PURE__*/React.createElement("text", {
|
|
28798
|
+
className: "axis-tick-label",
|
|
28799
|
+
style: {
|
|
28800
|
+
fontSize: tickLabelSize
|
|
28801
|
+
},
|
|
28802
|
+
textAnchor: "end",
|
|
28803
|
+
x: xPositionText,
|
|
28804
|
+
y: yPositionText
|
|
28805
|
+
}, y.toString()));
|
|
29004
28806
|
};
|
|
29005
28807
|
const XGridTick = ({
|
|
29006
28808
|
x,
|
|
29007
|
-
|
|
28809
|
+
range
|
|
29008
28810
|
}) => {
|
|
28811
|
+
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
28812
|
+
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
28813
|
+
// the ticks and labels would render outside of the clipping-mask.
|
|
29009
28814
|
let yPointOnAxis = 0;
|
|
29010
|
-
|
|
29011
|
-
// even if they are outside the graph's range.
|
|
29012
|
-
if (graphInfo.range[Y][MIN] > 0) {
|
|
28815
|
+
if (range[Y][MIN] > 0) {
|
|
29013
28816
|
// If the graph is on the positive side of the y-axis, lock the ticks to the top of the graph
|
|
29014
|
-
yPointOnAxis =
|
|
28817
|
+
yPointOnAxis = range[Y][MIN];
|
|
29015
28818
|
}
|
|
29016
|
-
if (
|
|
28819
|
+
if (range[Y][MAX] < 0) {
|
|
29017
28820
|
// If the graph is on the negative side of the x-axis, lock the ticks to the bottom of the graph
|
|
29018
|
-
yPointOnAxis =
|
|
28821
|
+
yPointOnAxis = range[Y][MAX];
|
|
29019
28822
|
}
|
|
28823
|
+
|
|
28824
|
+
// Convert the Vector2 coordinates to pixel coordinates
|
|
29020
28825
|
const pointOnAxis = [x, yPointOnAxis];
|
|
29021
28826
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
29022
28827
|
|
|
29023
|
-
// If the tick is on the edge of the graph's range, don't render it
|
|
29024
|
-
if (xPosition === -graphInfo.width / 2 || xPosition === graphInfo.width) {
|
|
29025
|
-
return null;
|
|
29026
|
-
}
|
|
29027
|
-
|
|
29028
28828
|
// Position of the start of the tick
|
|
29029
28829
|
const x1 = xPosition;
|
|
29030
28830
|
const y1 = yPosition + tickSize / 2;
|
|
@@ -29032,21 +28832,56 @@ const XGridTick = ({
|
|
|
29032
28832
|
// Position of the end of the tick
|
|
29033
28833
|
const x2 = xPosition;
|
|
29034
28834
|
const y2 = yPosition - tickSize / 2;
|
|
28835
|
+
|
|
28836
|
+
// Adjust the Y position of the x-axis labels based on
|
|
28837
|
+
// whether the x-axis is above, within, or below the graph
|
|
28838
|
+
const yAdjustment = range[Y][MAX] < 0 ? -tickLabelSize : tickLabelSize * 1.75;
|
|
28839
|
+
|
|
28840
|
+
// Adjust the X position of the x-axis labels based on
|
|
28841
|
+
// whether the label is positive or negative, in order to
|
|
28842
|
+
// account for the width of the negative sign
|
|
28843
|
+
const xAdjustment = x < 0 ? -2 : 0;
|
|
28844
|
+
|
|
28845
|
+
// Apply the adjustments to the x and y positions for the text
|
|
28846
|
+
const xPositionText = xPosition + xAdjustment;
|
|
28847
|
+
const yPositionText = yPosition + yAdjustment;
|
|
29035
28848
|
return /*#__PURE__*/React.createElement("g", {
|
|
29036
|
-
className: "
|
|
28849
|
+
className: "tick"
|
|
29037
28850
|
}, /*#__PURE__*/React.createElement("line", {
|
|
29038
28851
|
x1: x1,
|
|
29039
28852
|
y1: y1,
|
|
29040
28853
|
x2: x2,
|
|
29041
28854
|
y2: y2,
|
|
29042
|
-
|
|
29043
|
-
})
|
|
28855
|
+
className: "axis-tick"
|
|
28856
|
+
}), /*#__PURE__*/React.createElement("text", {
|
|
28857
|
+
className: "axis-tick-label",
|
|
28858
|
+
style: {
|
|
28859
|
+
fontSize: tickLabelSize
|
|
28860
|
+
},
|
|
28861
|
+
textAnchor: "middle",
|
|
28862
|
+
x: xPositionText,
|
|
28863
|
+
y: yPositionText
|
|
28864
|
+
}, x.toString()));
|
|
28865
|
+
};
|
|
28866
|
+
|
|
28867
|
+
// Determines whether to show the label for the given tick
|
|
28868
|
+
// Currently, the only condition is to hide the label at -1
|
|
28869
|
+
// on the y-axis when the x-axis is within the graph
|
|
28870
|
+
const shouldShowLabel = (number, range) => {
|
|
28871
|
+
let showLabel = true;
|
|
28872
|
+
|
|
28873
|
+
// If the x-axis is within the graph and the y-axis is at -1, hide the label
|
|
28874
|
+
if (range[X][MIN] < -1 && range[X][MAX] > 0 && number === -1) {
|
|
28875
|
+
showLabel = false;
|
|
28876
|
+
}
|
|
28877
|
+
return showLabel;
|
|
29044
28878
|
};
|
|
29045
28879
|
function generateTickLocations(tickStep, min, max) {
|
|
29046
28880
|
const ticks = [];
|
|
29047
28881
|
|
|
29048
28882
|
// Add ticks in the positive direction
|
|
29049
|
-
|
|
28883
|
+
const start = Math.max(min, 0);
|
|
28884
|
+
for (let _i = start + tickStep; _i < max; _i += tickStep) {
|
|
29050
28885
|
ticks.push(_i);
|
|
29051
28886
|
}
|
|
29052
28887
|
|
|
@@ -29061,34 +28896,34 @@ function generateTickLocations(tickStep, min, max) {
|
|
|
29061
28896
|
const AxisTicks = () => {
|
|
29062
28897
|
const {
|
|
29063
28898
|
tickStep,
|
|
29064
|
-
range
|
|
29065
|
-
width,
|
|
29066
|
-
height
|
|
28899
|
+
range
|
|
29067
28900
|
} = useGraphConfig();
|
|
29068
|
-
const graphInfo = {
|
|
29069
|
-
range,
|
|
29070
|
-
width,
|
|
29071
|
-
height
|
|
29072
|
-
};
|
|
29073
28901
|
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
29074
28902
|
const [xTickStep, yTickStep] = tickStep;
|
|
28903
|
+
|
|
28904
|
+
// Generate the tick locations & labels for the x and y axes
|
|
29075
28905
|
const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
|
|
29076
28906
|
const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
|
|
29077
28907
|
return /*#__PURE__*/React.createElement("g", {
|
|
29078
|
-
className: "axis-ticks"
|
|
28908
|
+
className: "axis-ticks",
|
|
28909
|
+
role: "presentation"
|
|
28910
|
+
}, /*#__PURE__*/React.createElement("g", {
|
|
28911
|
+
className: "y-axis-ticks"
|
|
29079
28912
|
}, yGridTicks.map(y => {
|
|
29080
28913
|
return /*#__PURE__*/React.createElement(YGridTick, {
|
|
29081
28914
|
y: y,
|
|
29082
28915
|
key: `y-grid-tick-${y}`,
|
|
29083
|
-
|
|
28916
|
+
range: range
|
|
29084
28917
|
});
|
|
29085
|
-
}),
|
|
28918
|
+
})), /*#__PURE__*/React.createElement("g", {
|
|
28919
|
+
className: "x-axis-ticks"
|
|
28920
|
+
}, xGridTicks.map(x => {
|
|
29086
28921
|
return /*#__PURE__*/React.createElement(XGridTick, {
|
|
29087
28922
|
x: x,
|
|
29088
28923
|
key: `x-grid-tick-${x}`,
|
|
29089
|
-
|
|
28924
|
+
range: range
|
|
29090
28925
|
});
|
|
29091
|
-
}));
|
|
28926
|
+
})));
|
|
29092
28927
|
};
|
|
29093
28928
|
|
|
29094
28929
|
/**
|
|
@@ -29109,12 +28944,10 @@ const axisOptions = (props, axisIndex) => {
|
|
|
29109
28944
|
};
|
|
29110
28945
|
};
|
|
29111
28946
|
const Grid = props => {
|
|
29112
|
-
return props.markings === "none" ? null : /*#__PURE__*/React.createElement(
|
|
28947
|
+
return props.markings === "none" ? null : /*#__PURE__*/React.createElement(Coordinates.Cartesian, {
|
|
29113
28948
|
xAxis: axisOptions(props, X),
|
|
29114
28949
|
yAxis: axisOptions(props, Y)
|
|
29115
|
-
})
|
|
29116
|
-
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
29117
|
-
props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)));
|
|
28950
|
+
});
|
|
29118
28951
|
};
|
|
29119
28952
|
|
|
29120
28953
|
// Displays a stylized open eye: lint warnings are visible
|
|
@@ -31851,7 +31684,8 @@ function UnlimitedPointGraph(props) {
|
|
|
31851
31684
|
const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
|
|
31852
31685
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
|
|
31853
31686
|
style: {
|
|
31854
|
-
fill: "rgba(0,0,0,0)"
|
|
31687
|
+
fill: "rgba(0,0,0,0)",
|
|
31688
|
+
cursor: "crosshair"
|
|
31855
31689
|
},
|
|
31856
31690
|
width: widthPx,
|
|
31857
31691
|
height: heightPx,
|
|
@@ -32035,6 +31869,22 @@ const MafsGraph = props => {
|
|
|
32035
31869
|
const uniqueId = React.useId();
|
|
32036
31870
|
const descriptionId = `interactive-graph-description-${uniqueId}`;
|
|
32037
31871
|
const graphRef = React.useRef(null);
|
|
31872
|
+
|
|
31873
|
+
// Set up the SVG attributes for the nested SVGs that help lock
|
|
31874
|
+
// the grid and graph elements to the bounds of the graph.
|
|
31875
|
+
const {
|
|
31876
|
+
viewboxX,
|
|
31877
|
+
viewboxY
|
|
31878
|
+
} = calculateNestedSVGCoords(state.range, width, height);
|
|
31879
|
+
const viewBox = `${viewboxX} ${viewboxY} ${width} ${height}`;
|
|
31880
|
+
const nestedSVGAttributes = {
|
|
31881
|
+
width,
|
|
31882
|
+
height,
|
|
31883
|
+
viewBox,
|
|
31884
|
+
preserveAspectRatio: "xMidYMin",
|
|
31885
|
+
x: viewboxX,
|
|
31886
|
+
y: viewboxY
|
|
31887
|
+
};
|
|
32038
31888
|
return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
|
|
32039
31889
|
value: {
|
|
32040
31890
|
range: state.range,
|
|
@@ -32056,7 +31906,7 @@ const MafsGraph = props => {
|
|
|
32056
31906
|
padding: "25px 25px 0 0",
|
|
32057
31907
|
boxSizing: "content-box",
|
|
32058
31908
|
marginLeft: "20px",
|
|
32059
|
-
marginBottom: "
|
|
31909
|
+
marginBottom: "30px",
|
|
32060
31910
|
pointerEvents: props.static ? "none" : "auto",
|
|
32061
31911
|
userSelect: "none",
|
|
32062
31912
|
width,
|
|
@@ -32092,7 +31942,7 @@ const MafsGraph = props => {
|
|
|
32092
31942
|
bottom: 0,
|
|
32093
31943
|
left: 0
|
|
32094
31944
|
}
|
|
32095
|
-
}, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null)
|
|
31945
|
+
}, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
|
|
32096
31946
|
preserveAspectRatio: false,
|
|
32097
31947
|
viewBox: {
|
|
32098
31948
|
x: state.range[X],
|
|
@@ -32103,37 +31953,59 @@ const MafsGraph = props => {
|
|
|
32103
31953
|
zoom: false,
|
|
32104
31954
|
width: width,
|
|
32105
31955
|
height: height
|
|
32106
|
-
}, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid, {
|
|
32107
|
-
tickStep: props.step,
|
|
31956
|
+
}, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, /*#__PURE__*/React.createElement(Grid, {
|
|
32108
31957
|
gridStep: props.gridStep,
|
|
32109
31958
|
range: state.range,
|
|
32110
31959
|
containerSizeClass: props.containerSizeClass,
|
|
32111
|
-
markings: props.markings
|
|
32112
|
-
|
|
31960
|
+
markings: props.markings,
|
|
31961
|
+
width: width,
|
|
31962
|
+
height: height
|
|
31963
|
+
})),
|
|
31964
|
+
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
31965
|
+
props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
|
|
32113
31966
|
lockedFigures: props.lockedFigures,
|
|
32114
31967
|
range: state.range
|
|
32115
31968
|
}), props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
|
|
32116
31969
|
state,
|
|
32117
31970
|
dispatch
|
|
32118
|
-
})))), renderGraphControls({
|
|
31971
|
+
}))))), renderGraphControls({
|
|
32119
31972
|
state,
|
|
32120
|
-
dispatch
|
|
31973
|
+
dispatch,
|
|
31974
|
+
width
|
|
32121
31975
|
})));
|
|
32122
31976
|
};
|
|
32123
|
-
const renderPointGraphControls = props => /*#__PURE__*/React.createElement(
|
|
31977
|
+
const renderPointGraphControls = props => /*#__PURE__*/React.createElement(View, {
|
|
31978
|
+
style: {
|
|
31979
|
+
flexDirection: "row",
|
|
31980
|
+
width: props.width
|
|
31981
|
+
}
|
|
31982
|
+
}, /*#__PURE__*/React.createElement(Button, {
|
|
32124
31983
|
kind: "secondary",
|
|
32125
31984
|
style: {
|
|
32126
31985
|
width: "100%",
|
|
32127
31986
|
marginLeft: "20px"
|
|
32128
31987
|
},
|
|
31988
|
+
tabIndex: 0,
|
|
32129
31989
|
onClick: () => {
|
|
32130
31990
|
props.dispatch(actions.pointGraph.addPoint([0, 0]));
|
|
32131
31991
|
}
|
|
32132
|
-
}, "Add Point")
|
|
31992
|
+
}, "Add Point"), (props.state.focusedPointIndex !== null || props.state.previouslyFocusedPointIndex !== null) && /*#__PURE__*/React.createElement(Button, {
|
|
31993
|
+
kind: "secondary",
|
|
31994
|
+
color: "destructive",
|
|
31995
|
+
tabIndex: 0,
|
|
31996
|
+
style: {
|
|
31997
|
+
width: "100%",
|
|
31998
|
+
marginLeft: "20px"
|
|
31999
|
+
},
|
|
32000
|
+
onClick: event => {
|
|
32001
|
+
props.dispatch(actions.pointGraph.removePoint(props.state.previouslyFocusedPointIndex));
|
|
32002
|
+
}
|
|
32003
|
+
}, "Remove Point"));
|
|
32133
32004
|
const renderGraphControls = props => {
|
|
32134
32005
|
const {
|
|
32135
32006
|
state,
|
|
32136
|
-
dispatch
|
|
32007
|
+
dispatch,
|
|
32008
|
+
width
|
|
32137
32009
|
} = props;
|
|
32138
32010
|
const {
|
|
32139
32011
|
type
|
|
@@ -32143,7 +32015,8 @@ const renderGraphControls = props => {
|
|
|
32143
32015
|
if (state.numPoints === "unlimited") {
|
|
32144
32016
|
return renderPointGraphControls({
|
|
32145
32017
|
state,
|
|
32146
|
-
dispatch
|
|
32018
|
+
dispatch,
|
|
32019
|
+
width
|
|
32147
32020
|
});
|
|
32148
32021
|
}
|
|
32149
32022
|
return null;
|
|
@@ -32151,6 +32024,56 @@ const renderGraphControls = props => {
|
|
|
32151
32024
|
return null;
|
|
32152
32025
|
}
|
|
32153
32026
|
};
|
|
32027
|
+
|
|
32028
|
+
// Calculate the difference between the min and max values of a range
|
|
32029
|
+
const getRangeDiff = range => {
|
|
32030
|
+
const [min, max] = range;
|
|
32031
|
+
return Math.abs(max - min);
|
|
32032
|
+
};
|
|
32033
|
+
|
|
32034
|
+
// We need to adjust the nested SVG viewbox x and Y values based on the range of the graph in order
|
|
32035
|
+
// to ensure that the graph is sized and positioned correctly within the Mafs SVG and the clipping mask.
|
|
32036
|
+
// Exported for testing.
|
|
32037
|
+
const calculateNestedSVGCoords = (range, width, height) => {
|
|
32038
|
+
// X RANGE
|
|
32039
|
+
let viewboxX = 0; // When xMin is 0, we want to use 0 as the viewboxX value
|
|
32040
|
+
const totalXRange = getRangeDiff(range[X]);
|
|
32041
|
+
const gridCellWidth = width / totalXRange;
|
|
32042
|
+
const minX = range[X][MIN];
|
|
32043
|
+
|
|
32044
|
+
// If xMin is entirely positive, we need to adjust the
|
|
32045
|
+
// viewboxX to be the grid cell width multiplied by xMin
|
|
32046
|
+
if (minX > 0) {
|
|
32047
|
+
viewboxX = gridCellWidth * Math.abs(minX);
|
|
32048
|
+
}
|
|
32049
|
+
// If xMin is negative, we need to adjust the viewboxX to be
|
|
32050
|
+
// the negative value of the grid cell width multiplied by xMin
|
|
32051
|
+
if (minX < 0) {
|
|
32052
|
+
viewboxX = -gridCellWidth * Math.abs(minX);
|
|
32053
|
+
}
|
|
32054
|
+
|
|
32055
|
+
// Y RANGE
|
|
32056
|
+
let viewboxY = -height; // When yMin is 0, we want to use the negative value of the graph height
|
|
32057
|
+
const totalYRange = getRangeDiff(range[Y]);
|
|
32058
|
+
const gridCellHeight = height / totalYRange;
|
|
32059
|
+
const minY = range[Y][MIN];
|
|
32060
|
+
|
|
32061
|
+
// If the y range is entirely positive, we want a negative sum of the
|
|
32062
|
+
// height and the gridcell height multiplied by the absolute value of yMin
|
|
32063
|
+
if (minY > 0) {
|
|
32064
|
+
viewboxY = -height - gridCellHeight * Math.abs(minY);
|
|
32065
|
+
}
|
|
32066
|
+
|
|
32067
|
+
// If the yMin is negative, we want to multiply the gridcell height
|
|
32068
|
+
// by the absolute value of yMin, and subtract the full height of the graph
|
|
32069
|
+
if (minY < 0) {
|
|
32070
|
+
viewboxY = gridCellHeight * Math.abs(minY) - height;
|
|
32071
|
+
}
|
|
32072
|
+
return {
|
|
32073
|
+
viewboxX,
|
|
32074
|
+
viewboxY
|
|
32075
|
+
};
|
|
32076
|
+
};
|
|
32154
32077
|
const renderGraph = props => {
|
|
32155
32078
|
const {
|
|
32156
32079
|
state,
|
|
@@ -32262,7 +32185,8 @@ function initializeGraphState(params) {
|
|
|
32262
32185
|
type: graph.type,
|
|
32263
32186
|
coords: getPointCoords(graph, range, step),
|
|
32264
32187
|
numPoints: graph.numPoints || 0,
|
|
32265
|
-
focusedPointIndex: null
|
|
32188
|
+
focusedPointIndex: null,
|
|
32189
|
+
previouslyFocusedPointIndex: null
|
|
32266
32190
|
});
|
|
32267
32191
|
case "circle":
|
|
32268
32192
|
return _extends({}, shared, {
|
|
@@ -32555,6 +32479,7 @@ function doBlurPoint(state, action) {
|
|
|
32555
32479
|
switch (state.type) {
|
|
32556
32480
|
case "point":
|
|
32557
32481
|
return _extends({}, state, {
|
|
32482
|
+
previouslyFocusedPointIndex: state.focusedPointIndex,
|
|
32558
32483
|
focusedPointIndex: null
|
|
32559
32484
|
});
|
|
32560
32485
|
default:
|
|
@@ -32599,7 +32524,6 @@ function doMovePointInFigure(state, action) {
|
|
|
32599
32524
|
}
|
|
32600
32525
|
case "angle":
|
|
32601
32526
|
case "circle":
|
|
32602
|
-
throw new Error("FIXME implement circle reducer");
|
|
32603
32527
|
case "point":
|
|
32604
32528
|
case "polygon":
|
|
32605
32529
|
case "quadratic":
|
|
@@ -32910,7 +32834,8 @@ function doRemovePoint(state, action) {
|
|
|
32910
32834
|
}
|
|
32911
32835
|
return _extends({}, state, {
|
|
32912
32836
|
coords: state.coords.filter((_, i) => i !== action.index),
|
|
32913
|
-
focusedPointIndex: null
|
|
32837
|
+
focusedPointIndex: null,
|
|
32838
|
+
previouslyFocusedPointIndex: null
|
|
32914
32839
|
});
|
|
32915
32840
|
}
|
|
32916
32841
|
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
@@ -39737,15 +39662,15 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
39737
39662
|
|
|
39738
39663
|
// Render the text labels
|
|
39739
39664
|
results.push(graphie.style(props.isMobile ? {
|
|
39740
|
-
color: KhanColors.
|
|
39665
|
+
color: KhanColors.BLUE
|
|
39741
39666
|
} : {}, () => _label(graphie, props.labelStyle, leftLabel, leftLabel, base)));
|
|
39742
39667
|
results.push(graphie.style(props.isMobile ? {
|
|
39743
|
-
color: KhanColors.
|
|
39668
|
+
color: KhanColors.BLUE
|
|
39744
39669
|
} : {}, () => _label(graphie, props.labelStyle, rightLabel, rightLabel, base)));
|
|
39745
39670
|
|
|
39746
39671
|
// Render the labels' lines
|
|
39747
39672
|
graphie.style({
|
|
39748
|
-
stroke:
|
|
39673
|
+
stroke: KhanColors.BLUE,
|
|
39749
39674
|
strokeWidth: 3.5
|
|
39750
39675
|
}, () => {
|
|
39751
39676
|
results.push(graphie.line([leftLabel, -0.2], [leftLabel, 0.2]));
|
|
@@ -39912,21 +39837,21 @@ class NumberLine extends React.Component {
|
|
|
39912
39837
|
if (isOpen) {
|
|
39913
39838
|
fill = KhanColors._BACKGROUND;
|
|
39914
39839
|
} else if (props.static) {
|
|
39915
|
-
fill = KhanColors.
|
|
39840
|
+
fill = KhanColors.BLUE;
|
|
39916
39841
|
} else {
|
|
39917
|
-
fill = KhanColors.
|
|
39842
|
+
fill = KhanColors.GREEN;
|
|
39918
39843
|
}
|
|
39919
39844
|
const normalStyle = {
|
|
39920
39845
|
fill: fill,
|
|
39921
|
-
stroke: props.static ? KhanColors.
|
|
39846
|
+
stroke: props.static ? KhanColors.BLUE : KhanColors.GREEN,
|
|
39922
39847
|
"stroke-width": isOpen ? 3 : 1
|
|
39923
39848
|
};
|
|
39924
39849
|
const highlightStyle = {
|
|
39925
|
-
fill: isOpen ? KhanColors._BACKGROUND : KhanColors.
|
|
39850
|
+
fill: isOpen ? KhanColors._BACKGROUND : KhanColors.GREEN,
|
|
39926
39851
|
"stroke-width": isOpen ? 3 : 1
|
|
39927
39852
|
};
|
|
39928
39853
|
const mobileDotStyle = props.isInequality ? {
|
|
39929
|
-
stroke: KhanColors.
|
|
39854
|
+
stroke: KhanColors.GREEN,
|
|
39930
39855
|
"fill-opacity": isOpen ? 0 : 1
|
|
39931
39856
|
} : {};
|
|
39932
39857
|
return /*#__PURE__*/React.createElement(MovablePoint
|
|
@@ -39984,7 +39909,7 @@ class NumberLine extends React.Component {
|
|
|
39984
39909
|
const end = this._getInequalityEndpoint(props);
|
|
39985
39910
|
const style = {
|
|
39986
39911
|
arrows: "->",
|
|
39987
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.
|
|
39912
|
+
stroke: this.props.apiOptions.isMobile ? KhanColors.GREEN : KhanColors.BLUE,
|
|
39988
39913
|
strokeWidth: 3.5
|
|
39989
39914
|
};
|
|
39990
39915
|
const isGreater = ["ge", "gt"].includes(props.rel);
|
|
@@ -44741,7 +44666,7 @@ const init = function init(options) {
|
|
|
44741
44666
|
|
|
44742
44667
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44743
44668
|
const libName = "@khanacademy/perseus";
|
|
44744
|
-
const libVersion = "33.
|
|
44669
|
+
const libVersion = "33.2.0";
|
|
44745
44670
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44746
44671
|
|
|
44747
44672
|
const apiVersion = {
|