@khanacademy/perseus 22.5.0 → 22.6.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 +69 -1
- package/dist/es/index.js +401 -106
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +69 -1
- package/dist/index.js +398 -100
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +1 -1
- package/dist/server-item-renderer.d.ts +1 -1
- package/dist/widgets/expression.d.ts +6 -6
- package/dist/widgets/interactive-graphs/axis-ticks.d.ts +1 -8
- package/dist/widgets/interactive-graphs/graphs/components/axis-tick-labels.d.ts +13 -0
- package/dist/widgets/interactive-graphs/graphs/use-transform.d.ts +2 -6
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +6 -0
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +12 -4
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +5 -6
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-state.d.ts +0 -1
- package/dist/widgets/interactive-graphs/reducer/use-graph-config.d.ts +3 -2
- package/dist/widgets/interactive-graphs/types.d.ts +5 -1
- package/dist/widgets/label-image.d.ts +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -28758,6 +28758,8 @@ class Graph extends React__namespace.Component {
|
|
|
28758
28758
|
|
|
28759
28759
|
const defaultGraphConfig = {
|
|
28760
28760
|
range: [[0, 1], [0, 1]],
|
|
28761
|
+
tickStep: [1, 1],
|
|
28762
|
+
gridStep: [1, 1],
|
|
28761
28763
|
snapStep: [1, 1],
|
|
28762
28764
|
markings: "none",
|
|
28763
28765
|
showTooltips: false,
|
|
@@ -28772,6 +28774,7 @@ function useGraphConfig() {
|
|
|
28772
28774
|
}
|
|
28773
28775
|
|
|
28774
28776
|
const matrixBuilder = mafs.vec.matrixBuilder;
|
|
28777
|
+
|
|
28775
28778
|
// When converting vectors, we don't need to translate because a vector
|
|
28776
28779
|
// is like a pair of x and y distances, which are not bound to specific graph locations.
|
|
28777
28780
|
function vectorsToPixels(vectors, graphState) {
|
|
@@ -29025,7 +29028,7 @@ function Arrowhead(props) {
|
|
|
29025
29028
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
29026
29029
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29027
29030
|
className: "interactive-graph-arrowhead",
|
|
29028
|
-
transform: `translate(${point[0]} ${point[1]}) rotate(${
|
|
29031
|
+
transform: `translate(${point[0]} ${point[1]}) rotate(${props.angle})`
|
|
29029
29032
|
}, /*#__PURE__*/React__namespace.createElement("g", {
|
|
29030
29033
|
transform: "translate(-1.5)"
|
|
29031
29034
|
}, /*#__PURE__*/React__namespace.createElement("path", {
|
|
@@ -29035,7 +29038,8 @@ function Arrowhead(props) {
|
|
|
29035
29038
|
stroke: props.color ?? "inherit"
|
|
29036
29039
|
},
|
|
29037
29040
|
strokeLinejoin: "round",
|
|
29038
|
-
strokeLinecap: "round"
|
|
29041
|
+
strokeLinecap: "round",
|
|
29042
|
+
strokeWidth: "2px"
|
|
29039
29043
|
})));
|
|
29040
29044
|
}
|
|
29041
29045
|
|
|
@@ -29072,10 +29076,7 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
|
29072
29076
|
case isBetween(xAtYExtreme, xMin, xMax):
|
|
29073
29077
|
return [xAtYExtreme, yExtreme];
|
|
29074
29078
|
default:
|
|
29075
|
-
|
|
29076
|
-
// (initialPoint is outside the graph bounds, or initialPoint and
|
|
29077
|
-
// throughPoint are the same).
|
|
29078
|
-
return [0, 0];
|
|
29079
|
+
return [xExtreme, yExtreme];
|
|
29079
29080
|
}
|
|
29080
29081
|
};
|
|
29081
29082
|
const getLines = points => {
|
|
@@ -29115,7 +29116,7 @@ function Vector(props) {
|
|
|
29115
29116
|
style
|
|
29116
29117
|
} = props;
|
|
29117
29118
|
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
29118
|
-
const direction = mafs.vec.sub(
|
|
29119
|
+
const direction = mafs.vec.sub(tipPx, tailPx);
|
|
29119
29120
|
const angle = calculateAngleInDegrees(direction);
|
|
29120
29121
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29121
29122
|
style: {
|
|
@@ -29159,7 +29160,9 @@ const LockedLine = props => {
|
|
|
29159
29160
|
} else {
|
|
29160
29161
|
const LineType = kind === "segment" ? mafs.Line.Segment : mafs.Line.ThroughPoints;
|
|
29161
29162
|
let arrowTip = kind === "segment" ? point2.coord : getIntersectionOfRayWithBox(point1.coord, point2.coord, range);
|
|
29162
|
-
|
|
29163
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
29164
|
+
const [tailPx, tipPx] = useTransformVectorsToPixels(point2.coord, point1.coord);
|
|
29165
|
+
const direction = mafs.vec.sub(tailPx, tipPx);
|
|
29163
29166
|
let angle = calculateAngleInDegrees(direction);
|
|
29164
29167
|
const startArrowHead = kind !== "segment" && /*#__PURE__*/React__namespace.createElement(Arrowhead, {
|
|
29165
29168
|
angle: angle,
|
|
@@ -29984,14 +29987,12 @@ function initializeGraphState(params) {
|
|
|
29984
29987
|
graph,
|
|
29985
29988
|
step,
|
|
29986
29989
|
snapStep,
|
|
29987
|
-
range
|
|
29988
|
-
markings
|
|
29990
|
+
range
|
|
29989
29991
|
} = params;
|
|
29990
29992
|
const shared = {
|
|
29991
29993
|
hasBeenInteractedWith: false,
|
|
29992
29994
|
range,
|
|
29993
|
-
snapStep
|
|
29994
|
-
markings
|
|
29995
|
+
snapStep
|
|
29995
29996
|
};
|
|
29996
29997
|
switch (graph.type) {
|
|
29997
29998
|
case "segment":
|
|
@@ -30186,10 +30187,16 @@ const getLineCoords = _ref3 => {
|
|
|
30186
30187
|
range,
|
|
30187
30188
|
step
|
|
30188
30189
|
} = _ref3;
|
|
30189
|
-
|
|
30190
|
-
|
|
30191
|
-
|
|
30192
|
-
|
|
30190
|
+
// Return two lines for a linear system, one for a ray or linear
|
|
30191
|
+
switch (graph.type) {
|
|
30192
|
+
case "linear-system":
|
|
30193
|
+
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
30194
|
+
case "linear":
|
|
30195
|
+
case "ray":
|
|
30196
|
+
return [normalizePoints(range, step, defaultLinearCoords[0])];
|
|
30197
|
+
default:
|
|
30198
|
+
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
30199
|
+
}
|
|
30193
30200
|
};
|
|
30194
30201
|
const getPolygonCoords = _ref4 => {
|
|
30195
30202
|
let {
|
|
@@ -30283,11 +30290,12 @@ function MovableCircle(props) {
|
|
|
30283
30290
|
cy: centerPx[1],
|
|
30284
30291
|
rx: radiiPx[0] + 3,
|
|
30285
30292
|
ry: radiiPx[1] + 3
|
|
30286
|
-
}), /*#__PURE__*/React__namespace.createElement(
|
|
30287
|
-
|
|
30288
|
-
|
|
30289
|
-
|
|
30290
|
-
|
|
30293
|
+
}), /*#__PURE__*/React__namespace.createElement("ellipse", {
|
|
30294
|
+
className: "circle",
|
|
30295
|
+
cx: centerPx[0],
|
|
30296
|
+
cy: centerPx[1],
|
|
30297
|
+
rx: radiiPx[0],
|
|
30298
|
+
ry: radiiPx[1]
|
|
30291
30299
|
}), /*#__PURE__*/React__namespace.createElement(DragHandle, {
|
|
30292
30300
|
center: center
|
|
30293
30301
|
}));
|
|
@@ -30328,6 +30336,260 @@ function crossProduct(as, bs) {
|
|
|
30328
30336
|
return result;
|
|
30329
30337
|
}
|
|
30330
30338
|
|
|
30339
|
+
// Universal padding for the axis ticks to ensure that the labels
|
|
30340
|
+
// have enough space to render without overlapping the tick lines
|
|
30341
|
+
const tickPadding = 5;
|
|
30342
|
+
const AxisTickLabels = () => {
|
|
30343
|
+
const graphConfig = useGraphConfig();
|
|
30344
|
+
const {
|
|
30345
|
+
tickStep,
|
|
30346
|
+
range
|
|
30347
|
+
} = graphConfig;
|
|
30348
|
+
const [xMin, xMax] = range[0];
|
|
30349
|
+
const [yMin, yMax] = range[1];
|
|
30350
|
+
const yTickStep = tickStep[1];
|
|
30351
|
+
const xTickStep = tickStep[0];
|
|
30352
|
+
const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
|
|
30353
|
+
const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
|
|
30354
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
30355
|
+
className: "axis-tick-labels"
|
|
30356
|
+
}, /*#__PURE__*/React__namespace.createElement(YGridAxis, {
|
|
30357
|
+
axisTicks: yGridTicks,
|
|
30358
|
+
graphConfig: graphConfig
|
|
30359
|
+
}), /*#__PURE__*/React__namespace.createElement(XGridAxis, {
|
|
30360
|
+
axisTicks: xGridTicks,
|
|
30361
|
+
graphConfig: graphConfig
|
|
30362
|
+
}));
|
|
30363
|
+
};
|
|
30364
|
+
|
|
30365
|
+
// Generate the necessary styles and ticks for the y-axis tick labels container
|
|
30366
|
+
const YGridAxis = props => {
|
|
30367
|
+
const {
|
|
30368
|
+
axisTicks,
|
|
30369
|
+
graphConfig
|
|
30370
|
+
} = props;
|
|
30371
|
+
const {
|
|
30372
|
+
width,
|
|
30373
|
+
height
|
|
30374
|
+
} = graphConfig;
|
|
30375
|
+
|
|
30376
|
+
// First get the label height by calculating the difference between the first two y-axis labels
|
|
30377
|
+
const yTopPoint = pointToPixel([0, axisTicks[0]], graphConfig);
|
|
30378
|
+
const ySecondPoint = pointToPixel([0, axisTicks[1]], graphConfig);
|
|
30379
|
+
const yLabelHeight = ySecondPoint[1] - yTopPoint[1];
|
|
30380
|
+
|
|
30381
|
+
// When rendering purely positive y-axis labels, we want to adjust the left position
|
|
30382
|
+
// from the lowest calculated tick, as yMin may be negative
|
|
30383
|
+
const yLowestTick = axisTicks[axisTicks.length - 1];
|
|
30384
|
+
const yLeftAdjustment = Math.min(Math.max(yTopPoint[0] - tickPadding, 0), width);
|
|
30385
|
+
|
|
30386
|
+
// We only want to render the 0 label on the y-axis if it is left of the grid
|
|
30387
|
+
const yAxisIsLeftOfGrid = yLeftAdjustment === 0;
|
|
30388
|
+
|
|
30389
|
+
// If the y-axis is right of the graph's viewbox/range,
|
|
30390
|
+
// we want to adjust both the positioning and text alignment
|
|
30391
|
+
const yAxisRightOfGrid = yLeftAdjustment === width;
|
|
30392
|
+
|
|
30393
|
+
// Determine the default spacing for the labels based on whether the y-axis is right of the grid
|
|
30394
|
+
const defaultSpacing = yAxisRightOfGrid ? 0.5 : 0;
|
|
30395
|
+
|
|
30396
|
+
// We also want to adjust the spacing if there are no negative y-axis labels as the extra space is not needed
|
|
30397
|
+
const marginSpacing = yLowestTick >= 0 ? defaultSpacing * 2 : defaultSpacing;
|
|
30398
|
+
|
|
30399
|
+
// Determine the text alignment based on whether the y-axis is right of the grid
|
|
30400
|
+
// We need to cast this as a const as it is used in the inline styles
|
|
30401
|
+
const textAlign = yAxisRightOfGrid ? "left" : "right";
|
|
30402
|
+
const yAxisStyles = {
|
|
30403
|
+
left: `calc(${yLeftAdjustment}px - ${marginSpacing}em)`,
|
|
30404
|
+
top: `calc(${yTopPoint[1]}px - 0.5em)`,
|
|
30405
|
+
height,
|
|
30406
|
+
textAlign,
|
|
30407
|
+
"--y-axis-label-height": yLabelHeight + "px"
|
|
30408
|
+
};
|
|
30409
|
+
const classNames = yAxisRightOfGrid ? "y-axis-tick-labels y-axis-right-of-grid" : "y-axis-tick-labels ";
|
|
30410
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
30411
|
+
className: classNames,
|
|
30412
|
+
"data-testid": "y-axis-tick-labels",
|
|
30413
|
+
style: yAxisStyles
|
|
30414
|
+
}, axisTicks.map(label => {
|
|
30415
|
+
return /*#__PURE__*/React__namespace.createElement(AxisTickLabel, {
|
|
30416
|
+
label: label,
|
|
30417
|
+
axis: "y",
|
|
30418
|
+
key: `y-grid-tick-${label}`,
|
|
30419
|
+
graphConfig: graphConfig,
|
|
30420
|
+
axisOutOfBounds: yAxisIsLeftOfGrid
|
|
30421
|
+
});
|
|
30422
|
+
}));
|
|
30423
|
+
};
|
|
30424
|
+
|
|
30425
|
+
// Generate the necessary styles and ticks for the x-axis tick labels container
|
|
30426
|
+
const XGridAxis = props => {
|
|
30427
|
+
const {
|
|
30428
|
+
axisTicks,
|
|
30429
|
+
graphConfig
|
|
30430
|
+
} = props;
|
|
30431
|
+
const {
|
|
30432
|
+
width,
|
|
30433
|
+
height
|
|
30434
|
+
} = graphConfig;
|
|
30435
|
+
|
|
30436
|
+
// Determine label width by calculating the difference between the first two x-axis labels
|
|
30437
|
+
const xFirstPoint = pointToPixel([axisTicks[0], 0], graphConfig);
|
|
30438
|
+
const xSecondPoint = pointToPixel([axisTicks[1], 0], graphConfig);
|
|
30439
|
+
const xLabelWidth = Math.abs(xSecondPoint[0] - xFirstPoint[0]);
|
|
30440
|
+
|
|
30441
|
+
// Determine the top position of the x-axis labels based on
|
|
30442
|
+
// whether the x-axis is above, within, or below the graph
|
|
30443
|
+
const xAxisAboveGraph = xFirstPoint[1] < 0;
|
|
30444
|
+
const xTopAdjustment = xAxisAboveGraph ? 0 : Math.min(xFirstPoint[1] + tickPadding, height);
|
|
30445
|
+
|
|
30446
|
+
// Determine whether the x-axis is outside of the graph's viewbox/range
|
|
30447
|
+
// This is used to conditionally render the 0 label on the x-axis
|
|
30448
|
+
const xAxisOutOfBounds = xTopAdjustment === height || xAxisAboveGraph;
|
|
30449
|
+
|
|
30450
|
+
// Determine the available space to the right of the x-axis labels
|
|
30451
|
+
const availableSpaceRight = width - xFirstPoint[0];
|
|
30452
|
+
|
|
30453
|
+
// Calculate the total combined width of ALL the x-axis labels
|
|
30454
|
+
const axisTicksWidth = axisTicks.length * xLabelWidth;
|
|
30455
|
+
|
|
30456
|
+
// Determine if we need to increase the width of the axis past the graph width
|
|
30457
|
+
const calculatedWidth = axisTicksWidth > width ? axisTicksWidth : width;
|
|
30458
|
+
|
|
30459
|
+
// Determine the right adjustment for the x-axis labels based on the available space
|
|
30460
|
+
// We're adjusting the right as the flex-direction is row-reverse
|
|
30461
|
+
const xRightAdjustment = availableSpaceRight - xLabelWidth / 2;
|
|
30462
|
+
const xAxisStyles = {
|
|
30463
|
+
top: xTopAdjustment,
|
|
30464
|
+
right: xRightAdjustment,
|
|
30465
|
+
width: calculatedWidth,
|
|
30466
|
+
"--x-axis-label-width": xLabelWidth + "px"
|
|
30467
|
+
};
|
|
30468
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
30469
|
+
className: `x-axis-tick-labels ${xAxisAboveGraph && "x-axis-top-of-grid"}`,
|
|
30470
|
+
"data-testid": "x-axis-tick-labels",
|
|
30471
|
+
style: xAxisStyles
|
|
30472
|
+
}, axisTicks.map(label => {
|
|
30473
|
+
return /*#__PURE__*/React__namespace.createElement(AxisTickLabel, {
|
|
30474
|
+
axis: "x",
|
|
30475
|
+
label: label,
|
|
30476
|
+
key: `x-grid-tick-${label}`,
|
|
30477
|
+
graphConfig: graphConfig,
|
|
30478
|
+
axisOutOfBounds: xAxisOutOfBounds
|
|
30479
|
+
});
|
|
30480
|
+
}));
|
|
30481
|
+
};
|
|
30482
|
+
|
|
30483
|
+
// Generate the individual tick label elements
|
|
30484
|
+
const AxisTickLabel = _ref => {
|
|
30485
|
+
let {
|
|
30486
|
+
label,
|
|
30487
|
+
graphConfig,
|
|
30488
|
+
axisOutOfBounds,
|
|
30489
|
+
axis
|
|
30490
|
+
} = _ref;
|
|
30491
|
+
const {
|
|
30492
|
+
gridStep,
|
|
30493
|
+
tickStep
|
|
30494
|
+
} = graphConfig;
|
|
30495
|
+
// Determine the point on the axis based on the axis type
|
|
30496
|
+
const pointOnAxis = axis === "x" ? [label, 0] : [0, label];
|
|
30497
|
+
const pixelPoint = pointToPixel(pointOnAxis, graphConfig);
|
|
30498
|
+
|
|
30499
|
+
// Remove the negative sign from the label string as it is handled in the data-content attribute
|
|
30500
|
+
const labelString = Math.abs(label).toString();
|
|
30501
|
+
|
|
30502
|
+
// Determine the relevant edge of the graph based on the axis type
|
|
30503
|
+
const graphEdge = axis === "x" ? graphConfig.width : graphConfig.height;
|
|
30504
|
+
|
|
30505
|
+
// Determine the vector index based on the axis type
|
|
30506
|
+
const vectorIndex = axis === "x" ? 0 : 1;
|
|
30507
|
+
|
|
30508
|
+
// Determine whether or not to render the tick label text
|
|
30509
|
+
const shouldShowLabel = showTickLabel({
|
|
30510
|
+
label,
|
|
30511
|
+
axisOutOfBounds,
|
|
30512
|
+
graphEdge,
|
|
30513
|
+
position: pixelPoint[vectorIndex],
|
|
30514
|
+
gridStep: gridStep[vectorIndex],
|
|
30515
|
+
tickStep: tickStep[vectorIndex]
|
|
30516
|
+
});
|
|
30517
|
+
|
|
30518
|
+
// Determine whether or not to render a negative symbol for the tick label
|
|
30519
|
+
// as the negative sign is handled in the data-content attribute in order to
|
|
30520
|
+
// maintain the correct spacing between the labels
|
|
30521
|
+
const dataContent = label < 0 && shouldShowLabel ? "−" : null;
|
|
30522
|
+
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
30523
|
+
className: `${axis}-axis-tick-label`,
|
|
30524
|
+
"data-content": dataContent
|
|
30525
|
+
}, shouldShowLabel && labelString);
|
|
30526
|
+
};
|
|
30527
|
+
|
|
30528
|
+
// Determine whether or not to show the tick label text
|
|
30529
|
+
const showTickLabel = _ref2 => {
|
|
30530
|
+
let {
|
|
30531
|
+
gridStep,
|
|
30532
|
+
tickStep,
|
|
30533
|
+
label,
|
|
30534
|
+
graphEdge,
|
|
30535
|
+
position,
|
|
30536
|
+
axisOutOfBounds
|
|
30537
|
+
} = _ref2;
|
|
30538
|
+
let showLabel = true;
|
|
30539
|
+
|
|
30540
|
+
// We only want to show the initial negative tick labels (e.g. -1) on each
|
|
30541
|
+
// axis if the tickStep > gridStep, to ensure that these labels do not overlap.
|
|
30542
|
+
// e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
|
|
30543
|
+
// which allows enough room for both these tick labels to render.
|
|
30544
|
+
showLabel = tickStep > gridStep ? true : label !== -tickStep;
|
|
30545
|
+
|
|
30546
|
+
// If the tick label position is greater than the relevant edge of the graph, don't render it
|
|
30547
|
+
if (position <= -graphEdge || position >= graphEdge) {
|
|
30548
|
+
showLabel = false;
|
|
30549
|
+
}
|
|
30550
|
+
|
|
30551
|
+
// If the label is 0, we only want to show it if the axis is to the left of the graph's viewbox/range
|
|
30552
|
+
// otherwise it will overlap with graph axis labels
|
|
30553
|
+
if (label === 0 && !axisOutOfBounds) {
|
|
30554
|
+
showLabel = false;
|
|
30555
|
+
}
|
|
30556
|
+
return showLabel;
|
|
30557
|
+
};
|
|
30558
|
+
|
|
30559
|
+
// Generate the tick labels for an axis based on the tick step
|
|
30560
|
+
function generateTickLocations$1(tickStep, min, max) {
|
|
30561
|
+
const positiveTicks = [];
|
|
30562
|
+
const negativeTicks = [];
|
|
30563
|
+
const ticks = [];
|
|
30564
|
+
|
|
30565
|
+
// Add 0 if it is applicable.
|
|
30566
|
+
// This will be conditionally rendered using showTickLabel.
|
|
30567
|
+
if (min <= 0 && max >= 0) {
|
|
30568
|
+
positiveTicks.push(0);
|
|
30569
|
+
}
|
|
30570
|
+
|
|
30571
|
+
// Add ticks on the positive axis
|
|
30572
|
+
// Start at the first tick after 0 or the minimum value,
|
|
30573
|
+
// taking into account that the graph may be zoomed in on the positive axis
|
|
30574
|
+
let i = Math.max(min, 0) + tickStep;
|
|
30575
|
+
for (i; min < i && i < max; i += tickStep) {
|
|
30576
|
+
positiveTicks.push(i);
|
|
30577
|
+
}
|
|
30578
|
+
|
|
30579
|
+
// Add ticks on the negative axis
|
|
30580
|
+
// Start at the first tick before 0 or the maximum value,
|
|
30581
|
+
// taking into account that the graph may be zoomed in on the negative axis
|
|
30582
|
+
i = Math.min(max, 0) - tickStep;
|
|
30583
|
+
for (i; i > min; i -= tickStep) {
|
|
30584
|
+
negativeTicks.push(i);
|
|
30585
|
+
}
|
|
30586
|
+
|
|
30587
|
+
// Reverse the ticks so that they are in the correct render order
|
|
30588
|
+
positiveTicks.reverse();
|
|
30589
|
+
ticks.push(...positiveTicks, ...negativeTicks);
|
|
30590
|
+
return ticks;
|
|
30591
|
+
}
|
|
30592
|
+
|
|
30331
30593
|
function PointGraph(props) {
|
|
30332
30594
|
const {
|
|
30333
30595
|
dispatch
|
|
@@ -30355,11 +30617,11 @@ function AxisArrows() {
|
|
|
30355
30617
|
}), /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
|
|
30356
30618
|
color: axisColor,
|
|
30357
30619
|
tip: [0, yMin],
|
|
30358
|
-
angle:
|
|
30620
|
+
angle: 90
|
|
30359
30621
|
}), /*#__PURE__*/React__default["default"].createElement(Arrowhead, {
|
|
30360
30622
|
color: axisColor,
|
|
30361
30623
|
tip: [0, yMax],
|
|
30362
|
-
angle:
|
|
30624
|
+
angle: 270
|
|
30363
30625
|
}));
|
|
30364
30626
|
}
|
|
30365
30627
|
|
|
@@ -30368,62 +30630,88 @@ const tickStyle = {
|
|
|
30368
30630
|
stroke: "black",
|
|
30369
30631
|
strokeWidth: 1
|
|
30370
30632
|
};
|
|
30371
|
-
|
|
30372
|
-
// We only want to show the initial negative tick labels (e.g. -1) on each
|
|
30373
|
-
// axis if the tickStep > gridStep, to ensure that these labels do not overlap.
|
|
30374
|
-
// e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
|
|
30375
|
-
// which allows enough room for both these tick labels to render.
|
|
30376
|
-
const showTickLabel = (gridStep, tickStep, label) => {
|
|
30377
|
-
const showLabel = tickStep > gridStep ? true : label !== -tickStep;
|
|
30378
|
-
return showLabel;
|
|
30379
|
-
};
|
|
30380
30633
|
const YGridTick = _ref => {
|
|
30381
30634
|
let {
|
|
30382
30635
|
y,
|
|
30383
|
-
|
|
30384
|
-
tickStep
|
|
30636
|
+
graphInfo
|
|
30385
30637
|
} = _ref;
|
|
30386
|
-
|
|
30638
|
+
let xPointOnAxis = 0;
|
|
30639
|
+
|
|
30640
|
+
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
30641
|
+
// even if they are outside the graph's range.
|
|
30642
|
+
if (graphInfo.range[0][0] > 0) {
|
|
30643
|
+
// If the graph is on the positive side of the x-axis, lock the ticks to the left side of the graph
|
|
30644
|
+
xPointOnAxis = graphInfo.range[0][0];
|
|
30645
|
+
}
|
|
30646
|
+
if (graphInfo.range[0][1] < 0) {
|
|
30647
|
+
// If the graph is on the negative side of the x-axis, lock the ticks to the right side of the graph
|
|
30648
|
+
xPointOnAxis = graphInfo.range[0][1];
|
|
30649
|
+
}
|
|
30650
|
+
const pointOnAxis = [xPointOnAxis, y];
|
|
30387
30651
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
30652
|
+
|
|
30653
|
+
// If the tick is on the edge of the graph's range, don't render it
|
|
30654
|
+
if (yPosition === -graphInfo.height || yPosition === graphInfo.height + 20) {
|
|
30655
|
+
return null;
|
|
30656
|
+
}
|
|
30657
|
+
|
|
30658
|
+
// Position of the start of the tick
|
|
30659
|
+
const x1 = xPosition - tickSize / 2;
|
|
30660
|
+
const y1 = yPosition;
|
|
30661
|
+
|
|
30662
|
+
// Position of the end of the tick
|
|
30663
|
+
const x2 = xPosition + tickSize / 2;
|
|
30664
|
+
const y2 = yPosition;
|
|
30388
30665
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30389
30666
|
className: "y-axis-ticks"
|
|
30390
30667
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
30391
|
-
x1:
|
|
30392
|
-
y1:
|
|
30393
|
-
x2:
|
|
30394
|
-
y2:
|
|
30668
|
+
x1: x1,
|
|
30669
|
+
y1: y1,
|
|
30670
|
+
x2: x2,
|
|
30671
|
+
y2: y2,
|
|
30395
30672
|
style: tickStyle
|
|
30396
|
-
})
|
|
30397
|
-
height: 20,
|
|
30398
|
-
width: 50,
|
|
30399
|
-
textAnchor: "end",
|
|
30400
|
-
x: xPosition - 10,
|
|
30401
|
-
y: yPosition + 5
|
|
30402
|
-
}, y.toString()));
|
|
30673
|
+
}));
|
|
30403
30674
|
};
|
|
30404
30675
|
const XGridTick = _ref2 => {
|
|
30405
30676
|
let {
|
|
30406
30677
|
x,
|
|
30407
|
-
|
|
30408
|
-
tickStep
|
|
30678
|
+
graphInfo
|
|
30409
30679
|
} = _ref2;
|
|
30410
|
-
|
|
30680
|
+
let yPointOnAxis = 0;
|
|
30681
|
+
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
30682
|
+
// even if they are outside the graph's range.
|
|
30683
|
+
if (graphInfo.range[1][0] > 0) {
|
|
30684
|
+
// If the graph is on the positive side of the y-axis, lock the ticks to the top of the graph
|
|
30685
|
+
yPointOnAxis = graphInfo.range[1][0];
|
|
30686
|
+
}
|
|
30687
|
+
if (graphInfo.range[1][1] < 0) {
|
|
30688
|
+
// If the graph is on the negative side of the x-axis, lock the ticks to the bottom of the graph
|
|
30689
|
+
yPointOnAxis = graphInfo.range[1][1];
|
|
30690
|
+
}
|
|
30691
|
+
const pointOnAxis = [x, yPointOnAxis];
|
|
30411
30692
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
30693
|
+
|
|
30694
|
+
// If the tick is on the edge of the graph's range, don't render it
|
|
30695
|
+
if (xPosition === -graphInfo.width / 2 || xPosition === graphInfo.width) {
|
|
30696
|
+
return null;
|
|
30697
|
+
}
|
|
30698
|
+
|
|
30699
|
+
// Position of the start of the tick
|
|
30700
|
+
const x1 = xPosition;
|
|
30701
|
+
const y1 = yPosition + tickSize / 2;
|
|
30702
|
+
|
|
30703
|
+
// Position of the end of the tick
|
|
30704
|
+
const x2 = xPosition;
|
|
30705
|
+
const y2 = yPosition - tickSize / 2;
|
|
30412
30706
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30413
30707
|
className: "x-axis-ticks"
|
|
30414
30708
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
30415
|
-
x1:
|
|
30416
|
-
y1:
|
|
30417
|
-
x2:
|
|
30418
|
-
y2:
|
|
30709
|
+
x1: x1,
|
|
30710
|
+
y1: y1,
|
|
30711
|
+
x2: x2,
|
|
30712
|
+
y2: y2,
|
|
30419
30713
|
style: tickStyle
|
|
30420
|
-
})
|
|
30421
|
-
height: 20,
|
|
30422
|
-
width: 50,
|
|
30423
|
-
textAnchor: "middle",
|
|
30424
|
-
x: xPosition,
|
|
30425
|
-
y: yPosition + 25
|
|
30426
|
-
}, x.toString()));
|
|
30714
|
+
}));
|
|
30427
30715
|
};
|
|
30428
30716
|
function generateTickLocations(tickStep, min, max) {
|
|
30429
30717
|
const ticks = [];
|
|
@@ -30434,17 +30722,29 @@ function generateTickLocations(tickStep, min, max) {
|
|
|
30434
30722
|
}
|
|
30435
30723
|
|
|
30436
30724
|
// Add ticks in the negative direction
|
|
30437
|
-
|
|
30725
|
+
// Start at the first tick after 0 or the maximum value if it is negative
|
|
30726
|
+
let i = Math.min(max, 0) - tickStep;
|
|
30727
|
+
for (i; i > min; i -= tickStep) {
|
|
30438
30728
|
ticks.push(i);
|
|
30439
30729
|
}
|
|
30440
30730
|
return ticks;
|
|
30441
30731
|
}
|
|
30442
|
-
const AxisTicks =
|
|
30443
|
-
const
|
|
30732
|
+
const AxisTicks = () => {
|
|
30733
|
+
const {
|
|
30734
|
+
tickStep,
|
|
30735
|
+
range,
|
|
30736
|
+
width,
|
|
30737
|
+
height
|
|
30738
|
+
} = useGraphConfig();
|
|
30739
|
+
const graphInfo = {
|
|
30740
|
+
range,
|
|
30741
|
+
width,
|
|
30742
|
+
height
|
|
30743
|
+
};
|
|
30444
30744
|
const [xMin, xMax] = range[0];
|
|
30445
30745
|
const [yMin, yMax] = range[1];
|
|
30446
|
-
const yTickStep =
|
|
30447
|
-
const xTickStep =
|
|
30746
|
+
const yTickStep = tickStep[1];
|
|
30747
|
+
const xTickStep = tickStep[0];
|
|
30448
30748
|
const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
|
|
30449
30749
|
const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
|
|
30450
30750
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
@@ -30453,15 +30753,13 @@ const AxisTicks = props => {
|
|
|
30453
30753
|
return /*#__PURE__*/React__namespace.createElement(YGridTick, {
|
|
30454
30754
|
y: y,
|
|
30455
30755
|
key: `y-grid-tick-${y}`,
|
|
30456
|
-
|
|
30457
|
-
tickStep: yTickStep
|
|
30756
|
+
graphInfo: graphInfo
|
|
30458
30757
|
});
|
|
30459
30758
|
}), xGridTicks.map(x => {
|
|
30460
30759
|
return /*#__PURE__*/React__namespace.createElement(XGridTick, {
|
|
30461
30760
|
x: x,
|
|
30462
30761
|
key: `x-grid-tick-${x}`,
|
|
30463
|
-
|
|
30464
|
-
tickStep: xTickStep
|
|
30762
|
+
graphInfo: graphInfo
|
|
30465
30763
|
});
|
|
30466
30764
|
}));
|
|
30467
30765
|
};
|
|
@@ -30489,11 +30787,7 @@ const Grid = props => {
|
|
|
30489
30787
|
yAxis: axisOptions(props, 1)
|
|
30490
30788
|
}),
|
|
30491
30789
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
30492
|
-
props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks,
|
|
30493
|
-
range: props.range,
|
|
30494
|
-
tickStep: props.tickStep,
|
|
30495
|
-
gridStep: props.gridStep
|
|
30496
|
-
}), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)));
|
|
30790
|
+
props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)));
|
|
30497
30791
|
};
|
|
30498
30792
|
|
|
30499
30793
|
// Displays a stylized open eye: lint warnings are visible
|
|
@@ -31519,35 +31813,22 @@ function mafsStateToInteractiveGraph(state) {
|
|
|
31519
31813
|
};
|
|
31520
31814
|
}
|
|
31521
31815
|
const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
31816
|
+
const {
|
|
31817
|
+
onChange
|
|
31818
|
+
} = props;
|
|
31522
31819
|
const [state, dispatch] = React__namespace.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
31523
31820
|
React.useImperativeHandle(ref, () => ({
|
|
31524
31821
|
getUserInput: () => getGradableGraph(state, props.graph)
|
|
31525
31822
|
}));
|
|
31526
|
-
function onChange(next) {
|
|
31527
|
-
props.onChange(mafsStateToInteractiveGraph(next));
|
|
31528
|
-
}
|
|
31529
|
-
return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({}, props, {
|
|
31530
|
-
state: state,
|
|
31531
|
-
dispatch: dispatch,
|
|
31532
|
-
onChange: onChange
|
|
31533
|
-
}));
|
|
31534
|
-
});
|
|
31535
|
-
const MafsGraph = props => {
|
|
31536
|
-
const {
|
|
31537
|
-
state,
|
|
31538
|
-
dispatch,
|
|
31539
|
-
labels
|
|
31540
|
-
} = props;
|
|
31541
|
-
const [width, height] = props.box;
|
|
31542
31823
|
const prevState = React.useRef(state);
|
|
31543
31824
|
React.useEffect(() => {
|
|
31544
31825
|
if (prevState.current !== state) {
|
|
31545
|
-
|
|
31826
|
+
onChange(mafsStateToInteractiveGraph({
|
|
31546
31827
|
graph: state
|
|
31547
|
-
});
|
|
31828
|
+
}));
|
|
31548
31829
|
}
|
|
31549
31830
|
prevState.current = state;
|
|
31550
|
-
}, [
|
|
31831
|
+
}, [onChange, state]);
|
|
31551
31832
|
|
|
31552
31833
|
// Destructuring first to keep useEffect from making excess calls
|
|
31553
31834
|
const [xSnap, ySnap] = props.snapStep;
|
|
@@ -31560,11 +31841,26 @@ const MafsGraph = props => {
|
|
|
31560
31841
|
React.useEffect(() => {
|
|
31561
31842
|
dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
|
|
31562
31843
|
}, [dispatch, xMinRange, xMaxRange, yMinRange, yMaxRange]);
|
|
31844
|
+
return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({}, props, {
|
|
31845
|
+
state: state,
|
|
31846
|
+
dispatch: dispatch
|
|
31847
|
+
}));
|
|
31848
|
+
});
|
|
31849
|
+
const MafsGraph = props => {
|
|
31850
|
+
const {
|
|
31851
|
+
state,
|
|
31852
|
+
dispatch,
|
|
31853
|
+
labels
|
|
31854
|
+
} = props;
|
|
31855
|
+
const [width, height] = props.box;
|
|
31856
|
+
const tickStep = props.step;
|
|
31563
31857
|
return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
|
|
31564
31858
|
value: {
|
|
31565
31859
|
range: state.range,
|
|
31566
31860
|
snapStep: state.snapStep,
|
|
31567
31861
|
markings: props.markings,
|
|
31862
|
+
tickStep: tickStep,
|
|
31863
|
+
gridStep: props.gridStep,
|
|
31568
31864
|
showTooltips: !!props.showTooltips,
|
|
31569
31865
|
graphDimensionsInPixels: props.box,
|
|
31570
31866
|
width,
|
|
@@ -31578,7 +31874,9 @@ const MafsGraph = props => {
|
|
|
31578
31874
|
height,
|
|
31579
31875
|
position: "relative",
|
|
31580
31876
|
padding: "25px 25px 0 0",
|
|
31581
|
-
boxSizing: "content-box"
|
|
31877
|
+
boxSizing: "content-box",
|
|
31878
|
+
marginLeft: "20px",
|
|
31879
|
+
marginBottom: "20px"
|
|
31582
31880
|
}
|
|
31583
31881
|
}, /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
|
|
31584
31882
|
box: props.box,
|
|
@@ -31589,11 +31887,11 @@ const MafsGraph = props => {
|
|
|
31589
31887
|
bottom: 0,
|
|
31590
31888
|
left: 0
|
|
31591
31889
|
}
|
|
31592
|
-
}, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(AxisLabels, null), /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
|
|
31890
|
+
}, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null), /*#__PURE__*/React__namespace.createElement(AxisTickLabels, null)), /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
|
|
31593
31891
|
preserveAspectRatio: false,
|
|
31594
31892
|
viewBox: {
|
|
31595
|
-
x:
|
|
31596
|
-
y:
|
|
31893
|
+
x: state.range[0],
|
|
31894
|
+
y: state.range[1],
|
|
31597
31895
|
padding: 0
|
|
31598
31896
|
},
|
|
31599
31897
|
pan: false,
|
|
@@ -31603,12 +31901,12 @@ const MafsGraph = props => {
|
|
|
31603
31901
|
}, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement(Grid, {
|
|
31604
31902
|
tickStep: props.step,
|
|
31605
31903
|
gridStep: props.gridStep,
|
|
31606
|
-
range:
|
|
31904
|
+
range: state.range,
|
|
31607
31905
|
containerSizeClass: props.containerSizeClass,
|
|
31608
31906
|
markings: props.markings
|
|
31609
31907
|
}), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
|
|
31610
31908
|
lockedFigures: props.lockedFigures,
|
|
31611
|
-
range:
|
|
31909
|
+
range: state.range
|
|
31612
31910
|
}), renderGraph({
|
|
31613
31911
|
state,
|
|
31614
31912
|
dispatch
|
|
@@ -43704,7 +44002,7 @@ const init = function (options) {
|
|
|
43704
44002
|
|
|
43705
44003
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43706
44004
|
const libName = "@khanacademy/perseus";
|
|
43707
|
-
const libVersion = "22.
|
|
44005
|
+
const libVersion = "22.6.0";
|
|
43708
44006
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43709
44007
|
|
|
43710
44008
|
const apiVersion = {
|