@khanacademy/perseus 22.5.1 → 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 +377 -79
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +69 -1
- package/dist/index.js +374 -73
- 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/reducer/use-graph-config.d.ts +3 -2
- package/dist/widgets/interactive-graphs/types.d.ts +5 -0
- package/dist/widgets/label-image.d.ts +2 -2
- package/package.json +2 -2
package/dist/es/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
|
|
|
29
29
|
import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
|
|
30
30
|
import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
|
|
31
31
|
import debounce$2 from 'lodash.debounce';
|
|
32
|
-
import { vec, Line as Line$4, Point as Point$2, useMovable, Text as Text$1, Polygon,
|
|
32
|
+
import { vec, Line as Line$4, Point as Point$2, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
|
|
33
33
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
34
34
|
import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
35
35
|
import questionIcon from '@phosphor-icons/core/regular/question.svg';
|
|
@@ -28474,6 +28474,8 @@ Graph.defaultProps = {
|
|
|
28474
28474
|
|
|
28475
28475
|
const defaultGraphConfig = {
|
|
28476
28476
|
range: [[0, 1], [0, 1]],
|
|
28477
|
+
tickStep: [1, 1],
|
|
28478
|
+
gridStep: [1, 1],
|
|
28477
28479
|
snapStep: [1, 1],
|
|
28478
28480
|
markings: "none",
|
|
28479
28481
|
showTooltips: false,
|
|
@@ -28488,6 +28490,7 @@ function useGraphConfig() {
|
|
|
28488
28490
|
}
|
|
28489
28491
|
|
|
28490
28492
|
const matrixBuilder = vec.matrixBuilder;
|
|
28493
|
+
|
|
28491
28494
|
// When converting vectors, we don't need to translate because a vector
|
|
28492
28495
|
// is like a pair of x and y distances, which are not bound to specific graph locations.
|
|
28493
28496
|
function vectorsToPixels(vectors, graphState, translation = [0, 0]) {
|
|
@@ -28736,7 +28739,7 @@ function Arrowhead(props) {
|
|
|
28736
28739
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
28737
28740
|
return /*#__PURE__*/React.createElement("g", {
|
|
28738
28741
|
className: "interactive-graph-arrowhead",
|
|
28739
|
-
transform: `translate(${point[0]} ${point[1]}) rotate(${
|
|
28742
|
+
transform: `translate(${point[0]} ${point[1]}) rotate(${props.angle})`
|
|
28740
28743
|
}, /*#__PURE__*/React.createElement("g", {
|
|
28741
28744
|
transform: "translate(-1.5)"
|
|
28742
28745
|
}, /*#__PURE__*/React.createElement("path", {
|
|
@@ -28746,7 +28749,8 @@ function Arrowhead(props) {
|
|
|
28746
28749
|
stroke: (_props$color = props.color) != null ? _props$color : "inherit"
|
|
28747
28750
|
},
|
|
28748
28751
|
strokeLinejoin: "round",
|
|
28749
|
-
strokeLinecap: "round"
|
|
28752
|
+
strokeLinecap: "round",
|
|
28753
|
+
strokeWidth: "2px"
|
|
28750
28754
|
})));
|
|
28751
28755
|
}
|
|
28752
28756
|
|
|
@@ -28782,10 +28786,7 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
|
28782
28786
|
case isBetween(xAtYExtreme, xMin, xMax):
|
|
28783
28787
|
return [xAtYExtreme, yExtreme];
|
|
28784
28788
|
default:
|
|
28785
|
-
|
|
28786
|
-
// (initialPoint is outside the graph bounds, or initialPoint and
|
|
28787
|
-
// throughPoint are the same).
|
|
28788
|
-
return [0, 0];
|
|
28789
|
+
return [xExtreme, yExtreme];
|
|
28789
28790
|
}
|
|
28790
28791
|
};
|
|
28791
28792
|
const getLines = points => {
|
|
@@ -28825,7 +28826,7 @@ function Vector(props) {
|
|
|
28825
28826
|
style
|
|
28826
28827
|
} = props;
|
|
28827
28828
|
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
28828
|
-
const direction = vec.sub(
|
|
28829
|
+
const direction = vec.sub(tipPx, tailPx);
|
|
28829
28830
|
const angle = calculateAngleInDegrees(direction);
|
|
28830
28831
|
return /*#__PURE__*/React.createElement("g", {
|
|
28831
28832
|
style: {
|
|
@@ -28869,7 +28870,9 @@ const LockedLine = props => {
|
|
|
28869
28870
|
} else {
|
|
28870
28871
|
const LineType = kind === "segment" ? Line$4.Segment : Line$4.ThroughPoints;
|
|
28871
28872
|
let arrowTip = kind === "segment" ? point2.coord : getIntersectionOfRayWithBox(point1.coord, point2.coord, range);
|
|
28872
|
-
|
|
28873
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
28874
|
+
const [tailPx, tipPx] = useTransformVectorsToPixels(point2.coord, point1.coord);
|
|
28875
|
+
const direction = vec.sub(tailPx, tipPx);
|
|
28873
28876
|
let angle = calculateAngleInDegrees(direction);
|
|
28874
28877
|
const startArrowHead = kind !== "segment" && /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28875
28878
|
angle: angle,
|
|
@@ -29867,19 +29870,24 @@ const getLineCoords = ({
|
|
|
29867
29870
|
range,
|
|
29868
29871
|
step
|
|
29869
29872
|
}) => {
|
|
29870
|
-
|
|
29871
|
-
|
|
29872
|
-
|
|
29873
|
-
|
|
29874
|
-
|
|
29873
|
+
// Return two lines for a linear system, one for a ray or linear
|
|
29874
|
+
switch (graph.type) {
|
|
29875
|
+
case "linear-system":
|
|
29876
|
+
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
29877
|
+
case "linear":
|
|
29878
|
+
case "ray":
|
|
29879
|
+
return [normalizePoints(range, step, defaultLinearCoords[0])];
|
|
29880
|
+
default:
|
|
29881
|
+
throw new UnreachableCaseError(graph);
|
|
29882
|
+
}
|
|
29875
29883
|
};
|
|
29876
29884
|
const getPolygonCoords = ({
|
|
29877
29885
|
graph,
|
|
29878
29886
|
range,
|
|
29879
29887
|
step
|
|
29880
29888
|
}) => {
|
|
29881
|
-
var _graph$
|
|
29882
|
-
let coords = (_graph$
|
|
29889
|
+
var _graph$coords2;
|
|
29890
|
+
let coords = (_graph$coords2 = graph.coords) == null ? void 0 : _graph$coords2.slice();
|
|
29883
29891
|
if (coords) {
|
|
29884
29892
|
return coords;
|
|
29885
29893
|
}
|
|
@@ -29965,11 +29973,12 @@ function MovableCircle(props) {
|
|
|
29965
29973
|
cy: centerPx[1],
|
|
29966
29974
|
rx: radiiPx[0] + 3,
|
|
29967
29975
|
ry: radiiPx[1] + 3
|
|
29968
|
-
}), /*#__PURE__*/React.createElement(
|
|
29969
|
-
|
|
29970
|
-
|
|
29971
|
-
|
|
29972
|
-
|
|
29976
|
+
}), /*#__PURE__*/React.createElement("ellipse", {
|
|
29977
|
+
className: "circle",
|
|
29978
|
+
cx: centerPx[0],
|
|
29979
|
+
cy: centerPx[1],
|
|
29980
|
+
rx: radiiPx[0],
|
|
29981
|
+
ry: radiiPx[1]
|
|
29973
29982
|
}), /*#__PURE__*/React.createElement(DragHandle, {
|
|
29974
29983
|
center: center
|
|
29975
29984
|
}));
|
|
@@ -30010,6 +30019,258 @@ function crossProduct(as, bs) {
|
|
|
30010
30019
|
return result;
|
|
30011
30020
|
}
|
|
30012
30021
|
|
|
30022
|
+
// Universal padding for the axis ticks to ensure that the labels
|
|
30023
|
+
// have enough space to render without overlapping the tick lines
|
|
30024
|
+
const tickPadding = 5;
|
|
30025
|
+
const AxisTickLabels = () => {
|
|
30026
|
+
const graphConfig = useGraphConfig();
|
|
30027
|
+
const {
|
|
30028
|
+
tickStep,
|
|
30029
|
+
range
|
|
30030
|
+
} = graphConfig;
|
|
30031
|
+
const [xMin, xMax] = range[0];
|
|
30032
|
+
const [yMin, yMax] = range[1];
|
|
30033
|
+
const yTickStep = tickStep[1];
|
|
30034
|
+
const xTickStep = tickStep[0];
|
|
30035
|
+
const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
|
|
30036
|
+
const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
|
|
30037
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
30038
|
+
className: "axis-tick-labels"
|
|
30039
|
+
}, /*#__PURE__*/React.createElement(YGridAxis, {
|
|
30040
|
+
axisTicks: yGridTicks,
|
|
30041
|
+
graphConfig: graphConfig
|
|
30042
|
+
}), /*#__PURE__*/React.createElement(XGridAxis, {
|
|
30043
|
+
axisTicks: xGridTicks,
|
|
30044
|
+
graphConfig: graphConfig
|
|
30045
|
+
}));
|
|
30046
|
+
};
|
|
30047
|
+
|
|
30048
|
+
// Generate the necessary styles and ticks for the y-axis tick labels container
|
|
30049
|
+
const YGridAxis = props => {
|
|
30050
|
+
const {
|
|
30051
|
+
axisTicks,
|
|
30052
|
+
graphConfig
|
|
30053
|
+
} = props;
|
|
30054
|
+
const {
|
|
30055
|
+
width,
|
|
30056
|
+
height
|
|
30057
|
+
} = graphConfig;
|
|
30058
|
+
|
|
30059
|
+
// First get the label height by calculating the difference between the first two y-axis labels
|
|
30060
|
+
const yTopPoint = pointToPixel([0, axisTicks[0]], graphConfig);
|
|
30061
|
+
const ySecondPoint = pointToPixel([0, axisTicks[1]], graphConfig);
|
|
30062
|
+
const yLabelHeight = ySecondPoint[1] - yTopPoint[1];
|
|
30063
|
+
|
|
30064
|
+
// When rendering purely positive y-axis labels, we want to adjust the left position
|
|
30065
|
+
// from the lowest calculated tick, as yMin may be negative
|
|
30066
|
+
const yLowestTick = axisTicks[axisTicks.length - 1];
|
|
30067
|
+
const yLeftAdjustment = Math.min(Math.max(yTopPoint[0] - tickPadding, 0), width);
|
|
30068
|
+
|
|
30069
|
+
// We only want to render the 0 label on the y-axis if it is left of the grid
|
|
30070
|
+
const yAxisIsLeftOfGrid = yLeftAdjustment === 0;
|
|
30071
|
+
|
|
30072
|
+
// If the y-axis is right of the graph's viewbox/range,
|
|
30073
|
+
// we want to adjust both the positioning and text alignment
|
|
30074
|
+
const yAxisRightOfGrid = yLeftAdjustment === width;
|
|
30075
|
+
|
|
30076
|
+
// Determine the default spacing for the labels based on whether the y-axis is right of the grid
|
|
30077
|
+
const defaultSpacing = yAxisRightOfGrid ? 0.5 : 0;
|
|
30078
|
+
|
|
30079
|
+
// We also want to adjust the spacing if there are no negative y-axis labels as the extra space is not needed
|
|
30080
|
+
const marginSpacing = yLowestTick >= 0 ? defaultSpacing * 2 : defaultSpacing;
|
|
30081
|
+
|
|
30082
|
+
// Determine the text alignment based on whether the y-axis is right of the grid
|
|
30083
|
+
// We need to cast this as a const as it is used in the inline styles
|
|
30084
|
+
const textAlign = yAxisRightOfGrid ? "left" : "right";
|
|
30085
|
+
const yAxisStyles = {
|
|
30086
|
+
left: `calc(${yLeftAdjustment}px - ${marginSpacing}em)`,
|
|
30087
|
+
top: `calc(${yTopPoint[1]}px - 0.5em)`,
|
|
30088
|
+
height,
|
|
30089
|
+
textAlign,
|
|
30090
|
+
"--y-axis-label-height": yLabelHeight + "px"
|
|
30091
|
+
};
|
|
30092
|
+
const classNames = yAxisRightOfGrid ? "y-axis-tick-labels y-axis-right-of-grid" : "y-axis-tick-labels ";
|
|
30093
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
30094
|
+
className: classNames,
|
|
30095
|
+
"data-testid": "y-axis-tick-labels",
|
|
30096
|
+
style: yAxisStyles
|
|
30097
|
+
}, axisTicks.map(label => {
|
|
30098
|
+
return /*#__PURE__*/React.createElement(AxisTickLabel, {
|
|
30099
|
+
label: label,
|
|
30100
|
+
axis: "y",
|
|
30101
|
+
key: `y-grid-tick-${label}`,
|
|
30102
|
+
graphConfig: graphConfig,
|
|
30103
|
+
axisOutOfBounds: yAxisIsLeftOfGrid
|
|
30104
|
+
});
|
|
30105
|
+
}));
|
|
30106
|
+
};
|
|
30107
|
+
|
|
30108
|
+
// Generate the necessary styles and ticks for the x-axis tick labels container
|
|
30109
|
+
const XGridAxis = props => {
|
|
30110
|
+
const {
|
|
30111
|
+
axisTicks,
|
|
30112
|
+
graphConfig
|
|
30113
|
+
} = props;
|
|
30114
|
+
const {
|
|
30115
|
+
width,
|
|
30116
|
+
height
|
|
30117
|
+
} = graphConfig;
|
|
30118
|
+
|
|
30119
|
+
// Determine label width by calculating the difference between the first two x-axis labels
|
|
30120
|
+
const xFirstPoint = pointToPixel([axisTicks[0], 0], graphConfig);
|
|
30121
|
+
const xSecondPoint = pointToPixel([axisTicks[1], 0], graphConfig);
|
|
30122
|
+
const xLabelWidth = Math.abs(xSecondPoint[0] - xFirstPoint[0]);
|
|
30123
|
+
|
|
30124
|
+
// Determine the top position of the x-axis labels based on
|
|
30125
|
+
// whether the x-axis is above, within, or below the graph
|
|
30126
|
+
const xAxisAboveGraph = xFirstPoint[1] < 0;
|
|
30127
|
+
const xTopAdjustment = xAxisAboveGraph ? 0 : Math.min(xFirstPoint[1] + tickPadding, height);
|
|
30128
|
+
|
|
30129
|
+
// Determine whether the x-axis is outside of the graph's viewbox/range
|
|
30130
|
+
// This is used to conditionally render the 0 label on the x-axis
|
|
30131
|
+
const xAxisOutOfBounds = xTopAdjustment === height || xAxisAboveGraph;
|
|
30132
|
+
|
|
30133
|
+
// Determine the available space to the right of the x-axis labels
|
|
30134
|
+
const availableSpaceRight = width - xFirstPoint[0];
|
|
30135
|
+
|
|
30136
|
+
// Calculate the total combined width of ALL the x-axis labels
|
|
30137
|
+
const axisTicksWidth = axisTicks.length * xLabelWidth;
|
|
30138
|
+
|
|
30139
|
+
// Determine if we need to increase the width of the axis past the graph width
|
|
30140
|
+
const calculatedWidth = axisTicksWidth > width ? axisTicksWidth : width;
|
|
30141
|
+
|
|
30142
|
+
// Determine the right adjustment for the x-axis labels based on the available space
|
|
30143
|
+
// We're adjusting the right as the flex-direction is row-reverse
|
|
30144
|
+
const xRightAdjustment = availableSpaceRight - xLabelWidth / 2;
|
|
30145
|
+
const xAxisStyles = {
|
|
30146
|
+
top: xTopAdjustment,
|
|
30147
|
+
right: xRightAdjustment,
|
|
30148
|
+
width: calculatedWidth,
|
|
30149
|
+
"--x-axis-label-width": xLabelWidth + "px"
|
|
30150
|
+
};
|
|
30151
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
30152
|
+
className: `x-axis-tick-labels ${xAxisAboveGraph && "x-axis-top-of-grid"}`,
|
|
30153
|
+
"data-testid": "x-axis-tick-labels",
|
|
30154
|
+
style: xAxisStyles
|
|
30155
|
+
}, axisTicks.map(label => {
|
|
30156
|
+
return /*#__PURE__*/React.createElement(AxisTickLabel, {
|
|
30157
|
+
axis: "x",
|
|
30158
|
+
label: label,
|
|
30159
|
+
key: `x-grid-tick-${label}`,
|
|
30160
|
+
graphConfig: graphConfig,
|
|
30161
|
+
axisOutOfBounds: xAxisOutOfBounds
|
|
30162
|
+
});
|
|
30163
|
+
}));
|
|
30164
|
+
};
|
|
30165
|
+
|
|
30166
|
+
// Generate the individual tick label elements
|
|
30167
|
+
const AxisTickLabel = ({
|
|
30168
|
+
label,
|
|
30169
|
+
graphConfig,
|
|
30170
|
+
axisOutOfBounds,
|
|
30171
|
+
axis
|
|
30172
|
+
}) => {
|
|
30173
|
+
const {
|
|
30174
|
+
gridStep,
|
|
30175
|
+
tickStep
|
|
30176
|
+
} = graphConfig;
|
|
30177
|
+
// Determine the point on the axis based on the axis type
|
|
30178
|
+
const pointOnAxis = axis === "x" ? [label, 0] : [0, label];
|
|
30179
|
+
const pixelPoint = pointToPixel(pointOnAxis, graphConfig);
|
|
30180
|
+
|
|
30181
|
+
// Remove the negative sign from the label string as it is handled in the data-content attribute
|
|
30182
|
+
const labelString = Math.abs(label).toString();
|
|
30183
|
+
|
|
30184
|
+
// Determine the relevant edge of the graph based on the axis type
|
|
30185
|
+
const graphEdge = axis === "x" ? graphConfig.width : graphConfig.height;
|
|
30186
|
+
|
|
30187
|
+
// Determine the vector index based on the axis type
|
|
30188
|
+
const vectorIndex = axis === "x" ? 0 : 1;
|
|
30189
|
+
|
|
30190
|
+
// Determine whether or not to render the tick label text
|
|
30191
|
+
const shouldShowLabel = showTickLabel({
|
|
30192
|
+
label,
|
|
30193
|
+
axisOutOfBounds,
|
|
30194
|
+
graphEdge,
|
|
30195
|
+
position: pixelPoint[vectorIndex],
|
|
30196
|
+
gridStep: gridStep[vectorIndex],
|
|
30197
|
+
tickStep: tickStep[vectorIndex]
|
|
30198
|
+
});
|
|
30199
|
+
|
|
30200
|
+
// Determine whether or not to render a negative symbol for the tick label
|
|
30201
|
+
// as the negative sign is handled in the data-content attribute in order to
|
|
30202
|
+
// maintain the correct spacing between the labels
|
|
30203
|
+
const dataContent = label < 0 && shouldShowLabel ? "−" : null;
|
|
30204
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
30205
|
+
className: `${axis}-axis-tick-label`,
|
|
30206
|
+
"data-content": dataContent
|
|
30207
|
+
}, shouldShowLabel && labelString);
|
|
30208
|
+
};
|
|
30209
|
+
|
|
30210
|
+
// Determine whether or not to show the tick label text
|
|
30211
|
+
const showTickLabel = ({
|
|
30212
|
+
gridStep,
|
|
30213
|
+
tickStep,
|
|
30214
|
+
label,
|
|
30215
|
+
graphEdge,
|
|
30216
|
+
position,
|
|
30217
|
+
axisOutOfBounds
|
|
30218
|
+
}) => {
|
|
30219
|
+
let showLabel = true;
|
|
30220
|
+
|
|
30221
|
+
// We only want to show the initial negative tick labels (e.g. -1) on each
|
|
30222
|
+
// axis if the tickStep > gridStep, to ensure that these labels do not overlap.
|
|
30223
|
+
// e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
|
|
30224
|
+
// which allows enough room for both these tick labels to render.
|
|
30225
|
+
showLabel = tickStep > gridStep ? true : label !== -tickStep;
|
|
30226
|
+
|
|
30227
|
+
// If the tick label position is greater than the relevant edge of the graph, don't render it
|
|
30228
|
+
if (position <= -graphEdge || position >= graphEdge) {
|
|
30229
|
+
showLabel = false;
|
|
30230
|
+
}
|
|
30231
|
+
|
|
30232
|
+
// If the label is 0, we only want to show it if the axis is to the left of the graph's viewbox/range
|
|
30233
|
+
// otherwise it will overlap with graph axis labels
|
|
30234
|
+
if (label === 0 && !axisOutOfBounds) {
|
|
30235
|
+
showLabel = false;
|
|
30236
|
+
}
|
|
30237
|
+
return showLabel;
|
|
30238
|
+
};
|
|
30239
|
+
|
|
30240
|
+
// Generate the tick labels for an axis based on the tick step
|
|
30241
|
+
function generateTickLocations$1(tickStep, min, max) {
|
|
30242
|
+
const positiveTicks = [];
|
|
30243
|
+
const negativeTicks = [];
|
|
30244
|
+
const ticks = [];
|
|
30245
|
+
|
|
30246
|
+
// Add 0 if it is applicable.
|
|
30247
|
+
// This will be conditionally rendered using showTickLabel.
|
|
30248
|
+
if (min <= 0 && max >= 0) {
|
|
30249
|
+
positiveTicks.push(0);
|
|
30250
|
+
}
|
|
30251
|
+
|
|
30252
|
+
// Add ticks on the positive axis
|
|
30253
|
+
// Start at the first tick after 0 or the minimum value,
|
|
30254
|
+
// taking into account that the graph may be zoomed in on the positive axis
|
|
30255
|
+
let i = Math.max(min, 0) + tickStep;
|
|
30256
|
+
for (i; min < i && i < max; i += tickStep) {
|
|
30257
|
+
positiveTicks.push(i);
|
|
30258
|
+
}
|
|
30259
|
+
|
|
30260
|
+
// Add ticks on the negative axis
|
|
30261
|
+
// Start at the first tick before 0 or the maximum value,
|
|
30262
|
+
// taking into account that the graph may be zoomed in on the negative axis
|
|
30263
|
+
i = Math.min(max, 0) - tickStep;
|
|
30264
|
+
for (i; i > min; i -= tickStep) {
|
|
30265
|
+
negativeTicks.push(i);
|
|
30266
|
+
}
|
|
30267
|
+
|
|
30268
|
+
// Reverse the ticks so that they are in the correct render order
|
|
30269
|
+
positiveTicks.reverse();
|
|
30270
|
+
ticks.push(...positiveTicks, ...negativeTicks);
|
|
30271
|
+
return ticks;
|
|
30272
|
+
}
|
|
30273
|
+
|
|
30013
30274
|
function PointGraph(props) {
|
|
30014
30275
|
const {
|
|
30015
30276
|
dispatch
|
|
@@ -30037,11 +30298,11 @@ function AxisArrows() {
|
|
|
30037
30298
|
}), /*#__PURE__*/React__default.createElement(Arrowhead, {
|
|
30038
30299
|
color: axisColor,
|
|
30039
30300
|
tip: [0, yMin],
|
|
30040
|
-
angle:
|
|
30301
|
+
angle: 90
|
|
30041
30302
|
}), /*#__PURE__*/React__default.createElement(Arrowhead, {
|
|
30042
30303
|
color: axisColor,
|
|
30043
30304
|
tip: [0, yMax],
|
|
30044
|
-
angle:
|
|
30305
|
+
angle: 270
|
|
30045
30306
|
}));
|
|
30046
30307
|
}
|
|
30047
30308
|
|
|
@@ -30050,81 +30311,119 @@ const tickStyle = {
|
|
|
30050
30311
|
stroke: "black",
|
|
30051
30312
|
strokeWidth: 1
|
|
30052
30313
|
};
|
|
30053
|
-
|
|
30054
|
-
// We only want to show the initial negative tick labels (e.g. -1) on each
|
|
30055
|
-
// axis if the tickStep > gridStep, to ensure that these labels do not overlap.
|
|
30056
|
-
// e.g. If gridStep = 1 and tickStep = 2, there are 2 grid lines for every 1 tick,
|
|
30057
|
-
// which allows enough room for both these tick labels to render.
|
|
30058
|
-
const showTickLabel = (gridStep, tickStep, label) => {
|
|
30059
|
-
const showLabel = tickStep > gridStep ? true : label !== -tickStep;
|
|
30060
|
-
return showLabel;
|
|
30061
|
-
};
|
|
30062
30314
|
const YGridTick = ({
|
|
30063
30315
|
y,
|
|
30064
|
-
|
|
30065
|
-
tickStep
|
|
30316
|
+
graphInfo
|
|
30066
30317
|
}) => {
|
|
30067
|
-
|
|
30318
|
+
let xPointOnAxis = 0;
|
|
30319
|
+
|
|
30320
|
+
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
30321
|
+
// even if they are outside the graph's range.
|
|
30322
|
+
if (graphInfo.range[0][0] > 0) {
|
|
30323
|
+
// If the graph is on the positive side of the x-axis, lock the ticks to the left side of the graph
|
|
30324
|
+
xPointOnAxis = graphInfo.range[0][0];
|
|
30325
|
+
}
|
|
30326
|
+
if (graphInfo.range[0][1] < 0) {
|
|
30327
|
+
// If the graph is on the negative side of the x-axis, lock the ticks to the right side of the graph
|
|
30328
|
+
xPointOnAxis = graphInfo.range[0][1];
|
|
30329
|
+
}
|
|
30330
|
+
const pointOnAxis = [xPointOnAxis, y];
|
|
30068
30331
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
30332
|
+
|
|
30333
|
+
// If the tick is on the edge of the graph's range, don't render it
|
|
30334
|
+
if (yPosition === -graphInfo.height || yPosition === graphInfo.height + 20) {
|
|
30335
|
+
return null;
|
|
30336
|
+
}
|
|
30337
|
+
|
|
30338
|
+
// Position of the start of the tick
|
|
30339
|
+
const x1 = xPosition - tickSize / 2;
|
|
30340
|
+
const y1 = yPosition;
|
|
30341
|
+
|
|
30342
|
+
// Position of the end of the tick
|
|
30343
|
+
const x2 = xPosition + tickSize / 2;
|
|
30344
|
+
const y2 = yPosition;
|
|
30069
30345
|
return /*#__PURE__*/React.createElement("g", {
|
|
30070
30346
|
className: "y-axis-ticks"
|
|
30071
30347
|
}, /*#__PURE__*/React.createElement("line", {
|
|
30072
|
-
x1:
|
|
30073
|
-
y1:
|
|
30074
|
-
x2:
|
|
30075
|
-
y2:
|
|
30348
|
+
x1: x1,
|
|
30349
|
+
y1: y1,
|
|
30350
|
+
x2: x2,
|
|
30351
|
+
y2: y2,
|
|
30076
30352
|
style: tickStyle
|
|
30077
|
-
})
|
|
30078
|
-
height: 20,
|
|
30079
|
-
width: 50,
|
|
30080
|
-
textAnchor: "end",
|
|
30081
|
-
x: xPosition - 10,
|
|
30082
|
-
y: yPosition + 5
|
|
30083
|
-
}, y.toString()));
|
|
30353
|
+
}));
|
|
30084
30354
|
};
|
|
30085
30355
|
const XGridTick = ({
|
|
30086
30356
|
x,
|
|
30087
|
-
|
|
30088
|
-
tickStep
|
|
30357
|
+
graphInfo
|
|
30089
30358
|
}) => {
|
|
30090
|
-
|
|
30359
|
+
let yPointOnAxis = 0;
|
|
30360
|
+
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
30361
|
+
// even if they are outside the graph's range.
|
|
30362
|
+
if (graphInfo.range[1][0] > 0) {
|
|
30363
|
+
// If the graph is on the positive side of the y-axis, lock the ticks to the top of the graph
|
|
30364
|
+
yPointOnAxis = graphInfo.range[1][0];
|
|
30365
|
+
}
|
|
30366
|
+
if (graphInfo.range[1][1] < 0) {
|
|
30367
|
+
// If the graph is on the negative side of the x-axis, lock the ticks to the bottom of the graph
|
|
30368
|
+
yPointOnAxis = graphInfo.range[1][1];
|
|
30369
|
+
}
|
|
30370
|
+
const pointOnAxis = [x, yPointOnAxis];
|
|
30091
30371
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
30372
|
+
|
|
30373
|
+
// If the tick is on the edge of the graph's range, don't render it
|
|
30374
|
+
if (xPosition === -graphInfo.width / 2 || xPosition === graphInfo.width) {
|
|
30375
|
+
return null;
|
|
30376
|
+
}
|
|
30377
|
+
|
|
30378
|
+
// Position of the start of the tick
|
|
30379
|
+
const x1 = xPosition;
|
|
30380
|
+
const y1 = yPosition + tickSize / 2;
|
|
30381
|
+
|
|
30382
|
+
// Position of the end of the tick
|
|
30383
|
+
const x2 = xPosition;
|
|
30384
|
+
const y2 = yPosition - tickSize / 2;
|
|
30092
30385
|
return /*#__PURE__*/React.createElement("g", {
|
|
30093
30386
|
className: "x-axis-ticks"
|
|
30094
30387
|
}, /*#__PURE__*/React.createElement("line", {
|
|
30095
|
-
x1:
|
|
30096
|
-
y1:
|
|
30097
|
-
x2:
|
|
30098
|
-
y2:
|
|
30388
|
+
x1: x1,
|
|
30389
|
+
y1: y1,
|
|
30390
|
+
x2: x2,
|
|
30391
|
+
y2: y2,
|
|
30099
30392
|
style: tickStyle
|
|
30100
|
-
})
|
|
30101
|
-
height: 20,
|
|
30102
|
-
width: 50,
|
|
30103
|
-
textAnchor: "middle",
|
|
30104
|
-
x: xPosition,
|
|
30105
|
-
y: yPosition + 25
|
|
30106
|
-
}, x.toString()));
|
|
30393
|
+
}));
|
|
30107
30394
|
};
|
|
30108
30395
|
function generateTickLocations(tickStep, min, max) {
|
|
30109
30396
|
const ticks = [];
|
|
30110
30397
|
|
|
30111
30398
|
// Add ticks in the positive direction
|
|
30112
|
-
for (let
|
|
30113
|
-
ticks.push(
|
|
30399
|
+
for (let _i = 0 + tickStep; _i < max; _i += tickStep) {
|
|
30400
|
+
ticks.push(_i);
|
|
30114
30401
|
}
|
|
30115
30402
|
|
|
30116
30403
|
// Add ticks in the negative direction
|
|
30117
|
-
|
|
30404
|
+
// Start at the first tick after 0 or the maximum value if it is negative
|
|
30405
|
+
let i = Math.min(max, 0) - tickStep;
|
|
30406
|
+
for (i; i > min; i -= tickStep) {
|
|
30118
30407
|
ticks.push(i);
|
|
30119
30408
|
}
|
|
30120
30409
|
return ticks;
|
|
30121
30410
|
}
|
|
30122
|
-
const AxisTicks =
|
|
30123
|
-
const
|
|
30411
|
+
const AxisTicks = () => {
|
|
30412
|
+
const {
|
|
30413
|
+
tickStep,
|
|
30414
|
+
range,
|
|
30415
|
+
width,
|
|
30416
|
+
height
|
|
30417
|
+
} = useGraphConfig();
|
|
30418
|
+
const graphInfo = {
|
|
30419
|
+
range,
|
|
30420
|
+
width,
|
|
30421
|
+
height
|
|
30422
|
+
};
|
|
30124
30423
|
const [xMin, xMax] = range[0];
|
|
30125
30424
|
const [yMin, yMax] = range[1];
|
|
30126
|
-
const yTickStep =
|
|
30127
|
-
const xTickStep =
|
|
30425
|
+
const yTickStep = tickStep[1];
|
|
30426
|
+
const xTickStep = tickStep[0];
|
|
30128
30427
|
const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
|
|
30129
30428
|
const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
|
|
30130
30429
|
return /*#__PURE__*/React.createElement("g", {
|
|
@@ -30133,15 +30432,13 @@ const AxisTicks = props => {
|
|
|
30133
30432
|
return /*#__PURE__*/React.createElement(YGridTick, {
|
|
30134
30433
|
y: y,
|
|
30135
30434
|
key: `y-grid-tick-${y}`,
|
|
30136
|
-
|
|
30137
|
-
tickStep: yTickStep
|
|
30435
|
+
graphInfo: graphInfo
|
|
30138
30436
|
});
|
|
30139
30437
|
}), xGridTicks.map(x => {
|
|
30140
30438
|
return /*#__PURE__*/React.createElement(XGridTick, {
|
|
30141
30439
|
x: x,
|
|
30142
30440
|
key: `x-grid-tick-${x}`,
|
|
30143
|
-
|
|
30144
|
-
tickStep: xTickStep
|
|
30441
|
+
graphInfo: graphInfo
|
|
30145
30442
|
});
|
|
30146
30443
|
}));
|
|
30147
30444
|
};
|
|
@@ -30169,11 +30466,7 @@ const Grid = props => {
|
|
|
30169
30466
|
yAxis: axisOptions(props, 1)
|
|
30170
30467
|
}),
|
|
30171
30468
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
30172
|
-
props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks,
|
|
30173
|
-
range: props.range,
|
|
30174
|
-
tickStep: props.tickStep,
|
|
30175
|
-
gridStep: props.gridStep
|
|
30176
|
-
}), /*#__PURE__*/React.createElement(AxisArrows, null)));
|
|
30469
|
+
props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)));
|
|
30177
30470
|
};
|
|
30178
30471
|
|
|
30179
30472
|
// Displays a stylized open eye: lint warnings are visible
|
|
@@ -31220,11 +31513,14 @@ const MafsGraph = props => {
|
|
|
31220
31513
|
labels
|
|
31221
31514
|
} = props;
|
|
31222
31515
|
const [width, height] = props.box;
|
|
31516
|
+
const tickStep = props.step;
|
|
31223
31517
|
return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
|
|
31224
31518
|
value: {
|
|
31225
31519
|
range: state.range,
|
|
31226
31520
|
snapStep: state.snapStep,
|
|
31227
31521
|
markings: props.markings,
|
|
31522
|
+
tickStep: tickStep,
|
|
31523
|
+
gridStep: props.gridStep,
|
|
31228
31524
|
showTooltips: !!props.showTooltips,
|
|
31229
31525
|
graphDimensionsInPixels: props.box,
|
|
31230
31526
|
width,
|
|
@@ -31238,7 +31534,9 @@ const MafsGraph = props => {
|
|
|
31238
31534
|
height,
|
|
31239
31535
|
position: "relative",
|
|
31240
31536
|
padding: "25px 25px 0 0",
|
|
31241
|
-
boxSizing: "content-box"
|
|
31537
|
+
boxSizing: "content-box",
|
|
31538
|
+
marginLeft: "20px",
|
|
31539
|
+
marginBottom: "20px"
|
|
31242
31540
|
}
|
|
31243
31541
|
}, /*#__PURE__*/React.createElement(LegacyGrid, {
|
|
31244
31542
|
box: props.box,
|
|
@@ -31249,7 +31547,7 @@ const MafsGraph = props => {
|
|
|
31249
31547
|
bottom: 0,
|
|
31250
31548
|
left: 0
|
|
31251
31549
|
}
|
|
31252
|
-
}, props.markings === "graph" && /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(Mafs, {
|
|
31550
|
+
}, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(AxisTickLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
|
|
31253
31551
|
preserveAspectRatio: false,
|
|
31254
31552
|
viewBox: {
|
|
31255
31553
|
x: state.range[0],
|
|
@@ -43369,7 +43667,7 @@ const init = function init(options) {
|
|
|
43369
43667
|
|
|
43370
43668
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43371
43669
|
const libName = "@khanacademy/perseus";
|
|
43372
|
-
const libVersion = "22.
|
|
43670
|
+
const libVersion = "22.6.0";
|
|
43373
43671
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43374
43672
|
|
|
43375
43673
|
const apiVersion = {
|