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