@khanacademy/perseus 56.0.0 → 56.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.css +14 -4
- package/dist/es/index.js +217 -61
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +14 -4
- package/dist/index.js +214 -59
- package/dist/index.js.map +1 -1
- package/dist/widgets/interactive-graph.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-line.d.ts +0 -1
- package/dist/widgets/interactive-graphs/graphs/components/vector.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/polygon.d.ts +6 -0
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-reducer.d.ts +1 -0
- package/dist/widgets/interactive-graphs/reducer/use-graph-config.d.ts +1 -0
- package/package.json +43 -43
package/dist/index.js
CHANGED
|
@@ -18350,17 +18350,6 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18350
18350
|
widget_id: this.props.id
|
|
18351
18351
|
},
|
|
18352
18352
|
onError: error => {
|
|
18353
|
-
// TODO(LEMS-2826): Remove analytics event in LEMS-2826 in favor of ti below.
|
|
18354
|
-
analytics.onAnalyticsEvent({
|
|
18355
|
-
type: "perseus:widget-rendering-error",
|
|
18356
|
-
payload: {
|
|
18357
|
-
widgetSubType: subType,
|
|
18358
|
-
widgetType: type,
|
|
18359
|
-
widgetId: this.props.id,
|
|
18360
|
-
message: error.message,
|
|
18361
|
-
userAgent: userAgent
|
|
18362
|
-
}
|
|
18363
|
-
});
|
|
18364
18353
|
analytics.onAnalyticsEvent({
|
|
18365
18354
|
type: "perseus:widget-rendering-error:ti",
|
|
18366
18355
|
payload: {
|
|
@@ -28143,7 +28132,8 @@ const defaultGraphConfig = {
|
|
|
28143
28132
|
width: 0,
|
|
28144
28133
|
height: 0,
|
|
28145
28134
|
labels: [],
|
|
28146
|
-
disableKeyboardInteraction: false
|
|
28135
|
+
disableKeyboardInteraction: false,
|
|
28136
|
+
interactiveColor: "var(--mafs-blue)"
|
|
28147
28137
|
};
|
|
28148
28138
|
const GraphConfigContext = /*#__PURE__*/React.createContext(defaultGraphConfig);
|
|
28149
28139
|
function useGraphConfig() {
|
|
@@ -28220,6 +28210,11 @@ const arrowPath = pathBuilder().move(-3, 4).curve(-2.75, 2.5, 0, 0.25, 0.75, 0).
|
|
|
28220
28210
|
function Arrowhead(props) {
|
|
28221
28211
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
28222
28212
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
28213
|
+
// Use aria-hidden to hide the line from screen readers
|
|
28214
|
+
// so it doesn't read as "image" with no context.
|
|
28215
|
+
// The elements using this should have their own aria-labels,
|
|
28216
|
+
// so this is okay.
|
|
28217
|
+
"aria-hidden": true,
|
|
28223
28218
|
className: "interactive-graph-arrowhead",
|
|
28224
28219
|
transform: `translate(${point[X]} ${point[Y]}) rotate(${props.angle})`
|
|
28225
28220
|
}, /*#__PURE__*/React__namespace.createElement("g", {
|
|
@@ -29389,6 +29384,11 @@ function SVGLine(props) {
|
|
|
29389
29384
|
testId
|
|
29390
29385
|
} = props;
|
|
29391
29386
|
return /*#__PURE__*/React__namespace.createElement("line", {
|
|
29387
|
+
// Use aria-hidden to hide the line from screen readers
|
|
29388
|
+
// so it doesn't read as "image" with no context.
|
|
29389
|
+
// The elements using this should have their own aria-labels,
|
|
29390
|
+
// so this is okay.
|
|
29391
|
+
"aria-hidden": true,
|
|
29392
29392
|
x1: start[X],
|
|
29393
29393
|
y1: start[Y],
|
|
29394
29394
|
x2: end[X],
|
|
@@ -29399,15 +29399,23 @@ function SVGLine(props) {
|
|
|
29399
29399
|
});
|
|
29400
29400
|
}
|
|
29401
29401
|
|
|
29402
|
+
/**
|
|
29403
|
+
* Used to visualize vectors for both interative elements
|
|
29404
|
+
* as well as locked figures within an Interactive Graph widget.
|
|
29405
|
+
*/
|
|
29402
29406
|
const {
|
|
29403
29407
|
calculateAngleInDegrees: calculateAngleInDegrees$3
|
|
29404
29408
|
} = kmath.angles;
|
|
29405
29409
|
function Vector(props) {
|
|
29410
|
+
const {
|
|
29411
|
+
interactiveColor
|
|
29412
|
+
} = useGraphConfig();
|
|
29406
29413
|
const {
|
|
29407
29414
|
tail,
|
|
29408
29415
|
tip,
|
|
29409
|
-
color,
|
|
29410
|
-
style
|
|
29416
|
+
color = interactiveColor,
|
|
29417
|
+
style,
|
|
29418
|
+
testId
|
|
29411
29419
|
} = props;
|
|
29412
29420
|
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
29413
29421
|
const direction = mafs.vec.sub(tipPx, tailPx);
|
|
@@ -29416,7 +29424,8 @@ function Vector(props) {
|
|
|
29416
29424
|
style: {
|
|
29417
29425
|
stroke: color,
|
|
29418
29426
|
strokeWidth: 2
|
|
29419
|
-
}
|
|
29427
|
+
},
|
|
29428
|
+
"data-testid": testId
|
|
29420
29429
|
}, /*#__PURE__*/React__namespace.createElement(SVGLine, {
|
|
29421
29430
|
start: tailPx,
|
|
29422
29431
|
end: tipPx,
|
|
@@ -29714,9 +29723,17 @@ function getAngleFromPoints(points, i) {
|
|
|
29714
29723
|
const a = mafs.vec.dist(point, pt1);
|
|
29715
29724
|
const b = mafs.vec.dist(point, pt2);
|
|
29716
29725
|
const c = mafs.vec.dist(pt1, pt2);
|
|
29726
|
+
let lawOfCosinesRadicand = (a ** 2 + b ** 2 - c ** 2) / (2 * a * b);
|
|
29727
|
+
|
|
29728
|
+
// If the equation results in a number greater than 1 or less than -1.
|
|
29729
|
+
// Correct to ensure a valid angle.
|
|
29730
|
+
// This ensures we are not producing NaN results from Math.acos.
|
|
29731
|
+
if (lawOfCosinesRadicand < -1 || lawOfCosinesRadicand > 1) {
|
|
29732
|
+
lawOfCosinesRadicand = Math.round(lawOfCosinesRadicand);
|
|
29733
|
+
}
|
|
29717
29734
|
|
|
29718
29735
|
// Law of cosines
|
|
29719
|
-
const angle = Math.acos(
|
|
29736
|
+
const angle = Math.acos(lawOfCosinesRadicand);
|
|
29720
29737
|
return angle;
|
|
29721
29738
|
}
|
|
29722
29739
|
function getSideLengthsFromPoints(points, i, isPolygonOpen) {
|
|
@@ -30243,9 +30260,17 @@ const PolygonAngle = _ref => {
|
|
|
30243
30260
|
const a = mafs.vec.dist(centerPoint, endPoints[0]);
|
|
30244
30261
|
const b = mafs.vec.dist(centerPoint, endPoints[1]);
|
|
30245
30262
|
const c = mafs.vec.dist(endPoints[0], endPoints[1]);
|
|
30263
|
+
let lawOfCosinesRadicand = (a ** 2 + b ** 2 - c ** 2) / (2 * a * b);
|
|
30264
|
+
|
|
30265
|
+
// If the equation results in a number greater than 1 or less than -1.
|
|
30266
|
+
// Correct to ensure a valid angle.
|
|
30267
|
+
// This ensures we are not producing NaN results from Math.acos.
|
|
30268
|
+
if (lawOfCosinesRadicand < -1 || lawOfCosinesRadicand > 1) {
|
|
30269
|
+
lawOfCosinesRadicand = Math.round(lawOfCosinesRadicand);
|
|
30270
|
+
}
|
|
30246
30271
|
|
|
30247
30272
|
// Law of cosines
|
|
30248
|
-
const angle = Math.acos(
|
|
30273
|
+
const angle = Math.acos(lawOfCosinesRadicand);
|
|
30249
30274
|
const y1 = centerY + (startY - centerY) / a * radius;
|
|
30250
30275
|
const x2 = centerX + (endX - centerX) / b * radius;
|
|
30251
30276
|
const x1 = centerX + (startX - centerX) / a * radius;
|
|
@@ -30288,7 +30313,7 @@ const PolygonAngle = _ref => {
|
|
|
30288
30313
|
}), /*#__PURE__*/React__namespace.createElement("feComposite", {
|
|
30289
30314
|
operator: "over",
|
|
30290
30315
|
in: "SourceGraphic"
|
|
30291
|
-
}))), !isOutside &&
|
|
30316
|
+
}))), !isOutside && isRightPolygonAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
|
|
30292
30317
|
start: [x1, y1],
|
|
30293
30318
|
vertex: [x2, y2],
|
|
30294
30319
|
end: [x3, y3]
|
|
@@ -30362,6 +30387,12 @@ const Angle = _ref2 => {
|
|
|
30362
30387
|
// Calculate the text position based on the angle and whether we allow reflex angles
|
|
30363
30388
|
// Let's try the angle bisector method to find the midpoint of the arc
|
|
30364
30389
|
const [textX, textY] = calculateBisectorPoint(point1, point2, vertex, isReflexive, allowReflexAngles, radius);
|
|
30390
|
+
|
|
30391
|
+
// Determine the color style for the arc when interactive vs disabled
|
|
30392
|
+
const {
|
|
30393
|
+
disableKeyboardInteraction
|
|
30394
|
+
} = useGraphConfig();
|
|
30395
|
+
const arcClassName = disableKeyboardInteraction ? "angle-arc-static" : "angle-arc-interactive";
|
|
30365
30396
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
|
|
30366
30397
|
id: "background",
|
|
30367
30398
|
x: "-5%",
|
|
@@ -30378,10 +30409,10 @@ const Angle = _ref2 => {
|
|
|
30378
30409
|
start: [x1, y1],
|
|
30379
30410
|
vertex: [x2, y2],
|
|
30380
30411
|
end: [x3, y3],
|
|
30381
|
-
className:
|
|
30412
|
+
className: arcClassName
|
|
30382
30413
|
}) : /*#__PURE__*/React__namespace.createElement(Arc, {
|
|
30383
30414
|
arc: arc,
|
|
30384
|
-
className:
|
|
30415
|
+
className: arcClassName
|
|
30385
30416
|
}), showAngles && /*#__PURE__*/React__namespace.createElement(TextLabel, {
|
|
30386
30417
|
x: textX,
|
|
30387
30418
|
y: textY,
|
|
@@ -30401,10 +30432,16 @@ const RightAngleSquare = _ref3 => {
|
|
|
30401
30432
|
className
|
|
30402
30433
|
} = _ref3;
|
|
30403
30434
|
return /*#__PURE__*/React__namespace.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React__namespace.createElement("path", {
|
|
30435
|
+
// Use aria-hidden to hide the line from screen readers
|
|
30436
|
+
// so it doesn't read as "image" with no context.
|
|
30437
|
+
// The elements using this should have their own aria-labels,
|
|
30438
|
+
// so this is okay.
|
|
30439
|
+
"aria-hidden": true,
|
|
30404
30440
|
d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
|
|
30405
30441
|
strokeWidth: 0.02,
|
|
30406
30442
|
fill: "none",
|
|
30407
|
-
className: className
|
|
30443
|
+
className: className,
|
|
30444
|
+
"data-testid": "angle-indicators__right-angle"
|
|
30408
30445
|
}));
|
|
30409
30446
|
};
|
|
30410
30447
|
|
|
@@ -30416,10 +30453,16 @@ const Arc = _ref4 => {
|
|
|
30416
30453
|
className
|
|
30417
30454
|
} = _ref4;
|
|
30418
30455
|
return /*#__PURE__*/React__namespace.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React__namespace.createElement("path", {
|
|
30456
|
+
// Use aria-hidden to hide the line from screen readers
|
|
30457
|
+
// so it doesn't read as "image" with no context.
|
|
30458
|
+
// The elements using this should have their own aria-labels,
|
|
30459
|
+
// so this is okay.
|
|
30460
|
+
"aria-hidden": true,
|
|
30419
30461
|
d: arc,
|
|
30420
30462
|
strokeWidth: 0.02,
|
|
30421
30463
|
fill: "none",
|
|
30422
|
-
className: className
|
|
30464
|
+
className: className,
|
|
30465
|
+
"data-testid": "angle-indicators__arc"
|
|
30423
30466
|
}));
|
|
30424
30467
|
};
|
|
30425
30468
|
const isRightPolygonAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
|
|
@@ -30692,7 +30735,13 @@ function Hairlines(props) {
|
|
|
30692
30735
|
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
30693
30736
|
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
30694
30737
|
const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
|
|
30695
|
-
return /*#__PURE__*/React__namespace.createElement("g",
|
|
30738
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30739
|
+
// Use aria-hidden to hide the line from screen readers
|
|
30740
|
+
// so it doesn't read as "image" with no context.
|
|
30741
|
+
// The elements using this should have their own aria-labels,
|
|
30742
|
+
// so this is okay.
|
|
30743
|
+
"aria-hidden": true
|
|
30744
|
+
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
30696
30745
|
x1: verticalStartX,
|
|
30697
30746
|
y1: y,
|
|
30698
30747
|
x2: verticalEndX,
|
|
@@ -30717,11 +30766,12 @@ const hitboxSizePx = 48;
|
|
|
30717
30766
|
const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
|
|
30718
30767
|
const {
|
|
30719
30768
|
markings,
|
|
30720
|
-
showTooltips
|
|
30769
|
+
showTooltips,
|
|
30770
|
+
interactiveColor
|
|
30721
30771
|
} = useGraphConfig();
|
|
30722
30772
|
const {
|
|
30723
30773
|
point,
|
|
30724
|
-
color =
|
|
30774
|
+
color = interactiveColor,
|
|
30725
30775
|
dragging,
|
|
30726
30776
|
focused,
|
|
30727
30777
|
cursor,
|
|
@@ -30739,6 +30789,11 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
|
|
|
30739
30789
|
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
30740
30790
|
const showHairlines = (dragging || focused) && markings !== "none";
|
|
30741
30791
|
const svgForPoint = /*#__PURE__*/React__namespace.createElement("g", {
|
|
30792
|
+
// Use aria-hidden to hide the line from screen readers
|
|
30793
|
+
// so it doesn't read as "image" with no context.
|
|
30794
|
+
// The elements using this should have their own aria-labels,
|
|
30795
|
+
// so this is okay.
|
|
30796
|
+
"aria-hidden": true,
|
|
30742
30797
|
ref: hitboxRef,
|
|
30743
30798
|
className: pointClasses,
|
|
30744
30799
|
style: {
|
|
@@ -30842,9 +30897,21 @@ function useControlPoint(params) {
|
|
|
30842
30897
|
x: srFormatNumber(point[X], locale),
|
|
30843
30898
|
y: srFormatNumber(point[Y], locale)
|
|
30844
30899
|
});
|
|
30900
|
+
|
|
30901
|
+
// Set the forwarded ref to the focusable handle element when it changes.
|
|
30845
30902
|
React.useLayoutEffect(() => {
|
|
30846
30903
|
setForwardedRef(forwardedRef, focusableHandleRef.current);
|
|
30847
30904
|
}, [forwardedRef]);
|
|
30905
|
+
|
|
30906
|
+
// If the point is being dragged and is not focused, focus the focusable handle.
|
|
30907
|
+
React.useLayoutEffect(() => {
|
|
30908
|
+
if (dragging && !focused) {
|
|
30909
|
+
// If the point is being dragged, focus the focusable handle so that
|
|
30910
|
+
// users can continue to interact with the point using the keyboard or buttons.
|
|
30911
|
+
// This particular focus call ensures that the focus ring and hairlines are visible.
|
|
30912
|
+
focusableHandleRef.current?.focus();
|
|
30913
|
+
}
|
|
30914
|
+
}, [dragging, focused]);
|
|
30848
30915
|
const focusableHandle = /*#__PURE__*/React__namespace.createElement("g", {
|
|
30849
30916
|
"data-testid": "movable-point__focusable-handle",
|
|
30850
30917
|
className: "movable-point__focusable-handle",
|
|
@@ -30905,7 +30972,6 @@ const MovableLine = props => {
|
|
|
30905
30972
|
points: [start, end],
|
|
30906
30973
|
ariaLabels,
|
|
30907
30974
|
ariaDescribedBy,
|
|
30908
|
-
color,
|
|
30909
30975
|
extend,
|
|
30910
30976
|
onMoveLine = () => {},
|
|
30911
30977
|
onMovePoint = () => {}
|
|
@@ -30940,7 +31006,6 @@ const MovableLine = props => {
|
|
|
30940
31006
|
ariaLive: ariaLives[0],
|
|
30941
31007
|
point: start,
|
|
30942
31008
|
sequenceNumber: 1,
|
|
30943
|
-
color,
|
|
30944
31009
|
onMove: p => {
|
|
30945
31010
|
setAriaLives(["polite", "off", "off"]);
|
|
30946
31011
|
onMovePoint(0, p);
|
|
@@ -30956,7 +31021,6 @@ const MovableLine = props => {
|
|
|
30956
31021
|
ariaLive: ariaLives[1],
|
|
30957
31022
|
point: end,
|
|
30958
31023
|
sequenceNumber: 2,
|
|
30959
|
-
color,
|
|
30960
31024
|
onMove: p => {
|
|
30961
31025
|
setAriaLives(["off", "polite", "off"]);
|
|
30962
31026
|
onMovePoint(1, p);
|
|
@@ -30969,7 +31033,6 @@ const MovableLine = props => {
|
|
|
30969
31033
|
ariaLive: ariaLives[2],
|
|
30970
31034
|
start: start,
|
|
30971
31035
|
end: end,
|
|
30972
|
-
stroke: color,
|
|
30973
31036
|
extend: extend,
|
|
30974
31037
|
onMove: delta => {
|
|
30975
31038
|
setAriaLives(["off", "off", "polite"]);
|
|
@@ -30978,7 +31041,6 @@ const MovableLine = props => {
|
|
|
30978
31041
|
});
|
|
30979
31042
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, focusableHandle1, line, focusableHandle2, visiblePoint1, visiblePoint2);
|
|
30980
31043
|
};
|
|
30981
|
-
const defaultStroke = "var(--movable-line-stroke-color)";
|
|
30982
31044
|
const Line$1 = props => {
|
|
30983
31045
|
const {
|
|
30984
31046
|
start,
|
|
@@ -30987,7 +31049,6 @@ const Line$1 = props => {
|
|
|
30987
31049
|
ariaDescribedBy,
|
|
30988
31050
|
ariaLive,
|
|
30989
31051
|
extend,
|
|
30990
|
-
stroke = defaultStroke,
|
|
30991
31052
|
onMove
|
|
30992
31053
|
} = props;
|
|
30993
31054
|
const [startPtPx, endPtPx] = useTransformVectorsToPixels(start, end);
|
|
@@ -30995,7 +31056,8 @@ const Line$1 = props => {
|
|
|
30995
31056
|
range,
|
|
30996
31057
|
graphDimensionsInPixels,
|
|
30997
31058
|
snapStep,
|
|
30998
|
-
disableKeyboardInteraction
|
|
31059
|
+
disableKeyboardInteraction,
|
|
31060
|
+
interactiveColor
|
|
30999
31061
|
} = useGraphConfig();
|
|
31000
31062
|
let startExtend = undefined;
|
|
31001
31063
|
let endExtend = undefined;
|
|
@@ -31052,7 +31114,7 @@ const Line$1 = props => {
|
|
|
31052
31114
|
start: startPtPx,
|
|
31053
31115
|
end: endPtPx,
|
|
31054
31116
|
style: {
|
|
31055
|
-
stroke,
|
|
31117
|
+
stroke: interactiveColor,
|
|
31056
31118
|
strokeWidth: "var(--movable-line-stroke-weight)"
|
|
31057
31119
|
},
|
|
31058
31120
|
className: dragging ? "movable-dragging" : "",
|
|
@@ -31060,11 +31122,11 @@ const Line$1 = props => {
|
|
|
31060
31122
|
})), startExtend && /*#__PURE__*/React__namespace.createElement(Vector, {
|
|
31061
31123
|
tail: start,
|
|
31062
31124
|
tip: startExtend,
|
|
31063
|
-
|
|
31125
|
+
testId: "movable-line__vector"
|
|
31064
31126
|
}), endExtend && /*#__PURE__*/React__namespace.createElement(Vector, {
|
|
31065
31127
|
tail: end,
|
|
31066
31128
|
tip: endExtend,
|
|
31067
|
-
|
|
31129
|
+
testId: "movable-line__vector"
|
|
31068
31130
|
}));
|
|
31069
31131
|
};
|
|
31070
31132
|
const getMovableLineKeyboardConstraint = (line, snapStep, pointIndex) => {
|
|
@@ -31137,7 +31199,8 @@ function AngleGraph(props) {
|
|
|
31137
31199
|
graphState
|
|
31138
31200
|
} = props;
|
|
31139
31201
|
const {
|
|
31140
|
-
graphDimensionsInPixels
|
|
31202
|
+
graphDimensionsInPixels,
|
|
31203
|
+
interactiveColor
|
|
31141
31204
|
} = useGraphConfig();
|
|
31142
31205
|
const i18n = usePerseusI18n();
|
|
31143
31206
|
const id = React__namespace.useId();
|
|
@@ -31169,13 +31232,14 @@ function AngleGraph(props) {
|
|
|
31169
31232
|
start: startPtPx,
|
|
31170
31233
|
end: endPtPx,
|
|
31171
31234
|
style: {
|
|
31172
|
-
stroke:
|
|
31235
|
+
stroke: interactiveColor,
|
|
31173
31236
|
strokeWidth: "var(--movable-line-stroke-weight)"
|
|
31174
|
-
}
|
|
31237
|
+
},
|
|
31238
|
+
testId: "angle-graph__line"
|
|
31175
31239
|
}), /*#__PURE__*/React__namespace.createElement(Vector, {
|
|
31176
31240
|
tail: angleLines[i][1],
|
|
31177
31241
|
tip: endExtend,
|
|
31178
|
-
|
|
31242
|
+
testId: "angle-graph__vector"
|
|
31179
31243
|
}));
|
|
31180
31244
|
});
|
|
31181
31245
|
|
|
@@ -31532,7 +31596,8 @@ function MovableCircle(props) {
|
|
|
31532
31596
|
} = props;
|
|
31533
31597
|
const {
|
|
31534
31598
|
snapStep,
|
|
31535
|
-
disableKeyboardInteraction
|
|
31599
|
+
disableKeyboardInteraction,
|
|
31600
|
+
interactiveColor
|
|
31536
31601
|
} = useGraphConfig();
|
|
31537
31602
|
const [focused, setFocused] = React__namespace.useState(false);
|
|
31538
31603
|
const draggableRef = React.useRef(null);
|
|
@@ -31568,7 +31633,9 @@ function MovableCircle(props) {
|
|
|
31568
31633
|
cx: centerPx[X],
|
|
31569
31634
|
cy: centerPx[Y],
|
|
31570
31635
|
rx: radiiPx[X],
|
|
31571
|
-
ry: radiiPx[Y]
|
|
31636
|
+
ry: radiiPx[Y],
|
|
31637
|
+
stroke: interactiveColor,
|
|
31638
|
+
"data-testid": "movable-circle__circle"
|
|
31572
31639
|
}), /*#__PURE__*/React__namespace.createElement(DragHandle, {
|
|
31573
31640
|
center: center,
|
|
31574
31641
|
dragging: dragging,
|
|
@@ -31585,7 +31652,8 @@ function DragHandle(props) {
|
|
|
31585
31652
|
} = props;
|
|
31586
31653
|
const [centerPx] = useTransformVectorsToPixels(center);
|
|
31587
31654
|
const {
|
|
31588
|
-
markings
|
|
31655
|
+
markings,
|
|
31656
|
+
interactiveColor
|
|
31589
31657
|
} = useGraphConfig();
|
|
31590
31658
|
const cornerRadius = Math.min(...dragHandleDimensions) / 2;
|
|
31591
31659
|
const topLeft = mafs.vec.sub(centerPx, mafs.vec.scale(dragHandleDimensions, 0.5));
|
|
@@ -31599,7 +31667,9 @@ function DragHandle(props) {
|
|
|
31599
31667
|
width: dragHandleDimensions[X],
|
|
31600
31668
|
height: dragHandleDimensions[Y],
|
|
31601
31669
|
rx: cornerRadius,
|
|
31602
|
-
ry: cornerRadius
|
|
31670
|
+
ry: cornerRadius,
|
|
31671
|
+
fill: interactiveColor,
|
|
31672
|
+
"data-testid": "movable-circle__handle"
|
|
31603
31673
|
}), dragHandleDotPositions.map(offsetPx => {
|
|
31604
31674
|
const [xPx, yPx] = mafs.vec.add(offsetPx, centerPx);
|
|
31605
31675
|
return /*#__PURE__*/React__namespace.createElement("circle", {
|
|
@@ -31758,8 +31828,7 @@ const LinearGraph = (props, key) => {
|
|
|
31758
31828
|
start: true,
|
|
31759
31829
|
end: true
|
|
31760
31830
|
},
|
|
31761
|
-
onMovePoint: (endpointIndex, destination) => dispatch(actions.linear.movePoint(endpointIndex, destination))
|
|
31762
|
-
color: "var(--movable-line-stroke-color)"
|
|
31831
|
+
onMovePoint: (endpointIndex, destination) => dispatch(actions.linear.movePoint(endpointIndex, destination))
|
|
31763
31832
|
}), /*#__PURE__*/React__namespace.createElement("g", {
|
|
31764
31833
|
id: pointsDescriptionId,
|
|
31765
31834
|
style: a11y.srOnly
|
|
@@ -31901,8 +31970,7 @@ const LinearSystemGraph = props => {
|
|
|
31901
31970
|
start: true,
|
|
31902
31971
|
end: true
|
|
31903
31972
|
},
|
|
31904
|
-
onMovePoint: (endpointIndex, destination) => dispatch(actions.linearSystem.movePointInFigure(i, endpointIndex, destination))
|
|
31905
|
-
color: "var(--movable-line-stroke-color)"
|
|
31973
|
+
onMovePoint: (endpointIndex, destination) => dispatch(actions.linearSystem.movePointInFigure(i, endpointIndex, destination))
|
|
31906
31974
|
})), linesAriaInfo.map(_ref2 => {
|
|
31907
31975
|
let {
|
|
31908
31976
|
pointsDescriptionId,
|
|
@@ -32748,6 +32816,7 @@ function doMovePoint(state, action) {
|
|
|
32748
32816
|
return {
|
|
32749
32817
|
...state,
|
|
32750
32818
|
hasBeenInteractedWith: true,
|
|
32819
|
+
focusedPointIndex: action.index,
|
|
32751
32820
|
coords: setAtIndex({
|
|
32752
32821
|
array: state.coords,
|
|
32753
32822
|
index: action.index,
|
|
@@ -33083,7 +33152,9 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
33083
33152
|
coords
|
|
33084
33153
|
} = _ref6;
|
|
33085
33154
|
const startingPoint = coords[index];
|
|
33086
|
-
|
|
33155
|
+
return calculateAngleSnap(destinationPoint, range, coords, index, startingPoint);
|
|
33156
|
+
}
|
|
33157
|
+
function calculateAngleSnap(destinationPoint, range, coords, index, startingPoint) {
|
|
33087
33158
|
// Needed to prevent updating the original coords before the checks for
|
|
33088
33159
|
// degenerate triangles and overlapping sides
|
|
33089
33160
|
const coordsCopy = [...coords];
|
|
@@ -33375,7 +33446,8 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
33375
33446
|
snapStep
|
|
33376
33447
|
} = statefulProps.graphState;
|
|
33377
33448
|
const {
|
|
33378
|
-
disableKeyboardInteraction
|
|
33449
|
+
disableKeyboardInteraction,
|
|
33450
|
+
interactiveColor
|
|
33379
33451
|
} = graphConfig;
|
|
33380
33452
|
const {
|
|
33381
33453
|
strings,
|
|
@@ -33411,12 +33483,16 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
33411
33483
|
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
33412
33484
|
}, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
33413
33485
|
points: [...points],
|
|
33414
|
-
color:
|
|
33486
|
+
color: interactiveColor,
|
|
33415
33487
|
svgPolygonProps: {
|
|
33416
33488
|
strokeWidth: focusVisible ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)",
|
|
33417
33489
|
style: {
|
|
33418
33490
|
fill: "transparent"
|
|
33419
|
-
}
|
|
33491
|
+
},
|
|
33492
|
+
// Use aria-hidden to hide the line from screen readers
|
|
33493
|
+
// so it doesn't read as "image" with no context.
|
|
33494
|
+
// This is okay because the graph has its own aria-label.
|
|
33495
|
+
"aria-hidden": true
|
|
33420
33496
|
}
|
|
33421
33497
|
}), points.map((point, i) => {
|
|
33422
33498
|
const pt1 = points.at(i - 1);
|
|
@@ -33557,6 +33633,9 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
33557
33633
|
strings,
|
|
33558
33634
|
locale
|
|
33559
33635
|
} = usePerseusI18n();
|
|
33636
|
+
const {
|
|
33637
|
+
interactiveColor
|
|
33638
|
+
} = useGraphConfig();
|
|
33560
33639
|
const id = React__namespace.useId();
|
|
33561
33640
|
const polygonPointsNumId = id + "-points-num";
|
|
33562
33641
|
const polygonPointsId = id + "-points";
|
|
@@ -33597,14 +33676,22 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
33597
33676
|
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
33598
33677
|
}, /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
|
|
33599
33678
|
points: [...points],
|
|
33600
|
-
color:
|
|
33679
|
+
color: interactiveColor,
|
|
33601
33680
|
svgPolylineProps: {
|
|
33602
33681
|
strokeWidth: "var(--movable-line-stroke-weight)",
|
|
33603
33682
|
style: {
|
|
33604
33683
|
fill: "transparent"
|
|
33605
|
-
}
|
|
33684
|
+
},
|
|
33685
|
+
// Use aria-hidden to hide the line from screen readers
|
|
33686
|
+
// so it doesn't read as "image" with no context.
|
|
33687
|
+
// This is okay because the graph has its own aria-label.
|
|
33688
|
+
"aria-hidden": true
|
|
33606
33689
|
}
|
|
33607
33690
|
}), /*#__PURE__*/React__namespace.createElement("rect", {
|
|
33691
|
+
// Use aria-hidden to hide the line from screen readers
|
|
33692
|
+
// so it doesn't read as "image" with no context.
|
|
33693
|
+
// This is okay because the graph has its own aria-label.
|
|
33694
|
+
"aria-hidden": true,
|
|
33608
33695
|
style: {
|
|
33609
33696
|
fill: "rgba(0,0,0,0)",
|
|
33610
33697
|
cursor: "crosshair"
|
|
@@ -33758,7 +33845,7 @@ function getKeyboardMovementConstraintForPoint(points, index, range, snapStep, s
|
|
|
33758
33845
|
case "sides":
|
|
33759
33846
|
return getSideSnapConstraint(points, index, range);
|
|
33760
33847
|
case "angles":
|
|
33761
|
-
return
|
|
33848
|
+
return getAngleSnapConstraint(points, index, range);
|
|
33762
33849
|
default:
|
|
33763
33850
|
throw new wonderStuffCore.UnreachableCaseError(snapTo);
|
|
33764
33851
|
}
|
|
@@ -33812,6 +33899,49 @@ function getSideSnapConstraint(points, index, range) {
|
|
|
33812
33899
|
right: movePointWithConstraint(coord => mafs.vec.add(coord, [1, 0]))
|
|
33813
33900
|
};
|
|
33814
33901
|
}
|
|
33902
|
+
function getAngleSnapConstraint(points, index, range) {
|
|
33903
|
+
// Make newPoints mutable.
|
|
33904
|
+
const newPoints = [...points];
|
|
33905
|
+
|
|
33906
|
+
// Get the point that is being moved.
|
|
33907
|
+
const pointToBeMoved = newPoints[index];
|
|
33908
|
+
|
|
33909
|
+
// Create a helper function that moves the point to a valid location
|
|
33910
|
+
// for angle snapping.
|
|
33911
|
+
const movePointWithConstraint = moveFunc => {
|
|
33912
|
+
// The direction the user is attempting to move the point in.
|
|
33913
|
+
let destinationAttempt = bound$1({
|
|
33914
|
+
snapStep: [0, 0],
|
|
33915
|
+
range,
|
|
33916
|
+
point: moveFunc(pointToBeMoved)
|
|
33917
|
+
});
|
|
33918
|
+
// The new point we're moving to.
|
|
33919
|
+
let newPoint = pointToBeMoved;
|
|
33920
|
+
|
|
33921
|
+
// Move the point and keep trying until we are at the boarder
|
|
33922
|
+
// of the graph.
|
|
33923
|
+
while (newPoint[0] === pointToBeMoved[0] && newPoint[1] === pointToBeMoved[1] && isInBound({
|
|
33924
|
+
range,
|
|
33925
|
+
point: destinationAttempt
|
|
33926
|
+
})) {
|
|
33927
|
+
newPoint = calculateAngleSnap(destinationAttempt, range, newPoints, index, pointToBeMoved);
|
|
33928
|
+
|
|
33929
|
+
// Increment the destinationAttempt.
|
|
33930
|
+
// For every time it does not work increment the direction for x and y.
|
|
33931
|
+
destinationAttempt = moveFunc(destinationAttempt);
|
|
33932
|
+
}
|
|
33933
|
+
return newPoint;
|
|
33934
|
+
};
|
|
33935
|
+
|
|
33936
|
+
// For each direction look for the next movable point by a small step to increase changes
|
|
33937
|
+
// of finding the next angle value.
|
|
33938
|
+
return {
|
|
33939
|
+
up: movePointWithConstraint(coord => mafs.vec.add(coord, [0, 0.1])),
|
|
33940
|
+
down: movePointWithConstraint(coord => mafs.vec.sub(coord, [0, 0.1])),
|
|
33941
|
+
left: movePointWithConstraint(coord => mafs.vec.sub(coord, [0.1, 0])),
|
|
33942
|
+
right: movePointWithConstraint(coord => mafs.vec.add(coord, [0.1, 0]))
|
|
33943
|
+
};
|
|
33944
|
+
}
|
|
33815
33945
|
|
|
33816
33946
|
function renderQuadraticGraph(state, dispatch, i18n) {
|
|
33817
33947
|
return {
|
|
@@ -33831,6 +33961,9 @@ function QuadraticGraph(props) {
|
|
|
33831
33961
|
coords,
|
|
33832
33962
|
snapStep
|
|
33833
33963
|
} = graphState;
|
|
33964
|
+
const {
|
|
33965
|
+
interactiveColor
|
|
33966
|
+
} = useGraphConfig();
|
|
33834
33967
|
const {
|
|
33835
33968
|
strings,
|
|
33836
33969
|
locale
|
|
@@ -33872,7 +34005,13 @@ function QuadraticGraph(props) {
|
|
|
33872
34005
|
"aria-describedby": `${quadraticDirectionId} ${quadraticVertexId} ${quadraticInterceptsId}`
|
|
33873
34006
|
}, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
|
|
33874
34007
|
y: y,
|
|
33875
|
-
color:
|
|
34008
|
+
color: interactiveColor,
|
|
34009
|
+
svgPathProps: {
|
|
34010
|
+
// Use aria-hidden to hide the line from screen readers
|
|
34011
|
+
// so it doesn't read as "image" with no context.
|
|
34012
|
+
// This is okay because the graph has its own aria-label.
|
|
34013
|
+
"aria-hidden": true
|
|
34014
|
+
}
|
|
33876
34015
|
}), coords.map((coord, i) => {
|
|
33877
34016
|
const srQuadraticPoint = getQuadraticPointString(i + 1, coord, strings, locale);
|
|
33878
34017
|
// Vertex might not exist if the quadratic graph is a line
|
|
@@ -34272,6 +34411,9 @@ function SinusoidGraph(props) {
|
|
|
34272
34411
|
dispatch,
|
|
34273
34412
|
graphState
|
|
34274
34413
|
} = props;
|
|
34414
|
+
const {
|
|
34415
|
+
interactiveColor
|
|
34416
|
+
} = useGraphConfig();
|
|
34275
34417
|
const i18n = usePerseusI18n();
|
|
34276
34418
|
const id = React__namespace.useId();
|
|
34277
34419
|
const descriptionId = id + "-description";
|
|
@@ -34314,7 +34456,13 @@ function SinusoidGraph(props) {
|
|
|
34314
34456
|
"aria-describedby": descriptionId
|
|
34315
34457
|
}, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
|
|
34316
34458
|
y: x => computeSine(x, coeffRef.current),
|
|
34317
|
-
color:
|
|
34459
|
+
color: interactiveColor,
|
|
34460
|
+
svgPathProps: {
|
|
34461
|
+
// Use aria-hidden to hide the line from screen readers
|
|
34462
|
+
// so it doesn't read as "image" with no context.
|
|
34463
|
+
// This is okay because the graph has its own aria-label.
|
|
34464
|
+
"aria-hidden": true
|
|
34465
|
+
}
|
|
34318
34466
|
}), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34319
34467
|
ariaLabel: i === 0 ? srSinusoidRootPoint : srSinusoidPeakPoint,
|
|
34320
34468
|
key: "point-" + i,
|
|
@@ -34663,6 +34811,7 @@ const MafsGraph = props => {
|
|
|
34663
34811
|
i18n,
|
|
34664
34812
|
markings: props.markings
|
|
34665
34813
|
});
|
|
34814
|
+
const disableInteraction = readOnly || !!props.static;
|
|
34666
34815
|
return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
|
|
34667
34816
|
value: {
|
|
34668
34817
|
range: state.range,
|
|
@@ -34675,7 +34824,12 @@ const MafsGraph = props => {
|
|
|
34675
34824
|
width,
|
|
34676
34825
|
height,
|
|
34677
34826
|
labels,
|
|
34678
|
-
disableKeyboardInteraction:
|
|
34827
|
+
disableKeyboardInteraction: disableInteraction,
|
|
34828
|
+
// If the graph is read-only or static, we want to make it
|
|
34829
|
+
// visually clear that the graph is no longer interactive.
|
|
34830
|
+
// We do this by changing the color of the interactive elements
|
|
34831
|
+
// to a static gray color rather than our standard blue.
|
|
34832
|
+
interactiveColor: disableInteraction ? "var(--static-gray)" : "var(--mafs-blue)"
|
|
34679
34833
|
}
|
|
34680
34834
|
}, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
34681
34835
|
className: "mafs-graph-container"
|
|
@@ -35701,7 +35855,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
35701
35855
|
const staticTransform$3 = ___default["default"].identity;
|
|
35702
35856
|
var InteractiveGraph$1 = {
|
|
35703
35857
|
name: "interactive-graph",
|
|
35704
|
-
displayName: "Interactive graph
|
|
35858
|
+
displayName: "Interactive graph",
|
|
35859
|
+
accessible: true,
|
|
35705
35860
|
widget: InteractiveGraph,
|
|
35706
35861
|
staticTransform: staticTransform$3
|
|
35707
35862
|
};
|
|
@@ -41796,7 +41951,7 @@ const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
|
|
|
41796
41951
|
|
|
41797
41952
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
41798
41953
|
const libName = "@khanacademy/perseus";
|
|
41799
|
-
const libVersion = "56.
|
|
41954
|
+
const libVersion = "56.2.0";
|
|
41800
41955
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
41801
41956
|
|
|
41802
41957
|
const apiVersion = {
|