@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/es/index.js
CHANGED
|
@@ -18020,17 +18020,6 @@ class WidgetContainer extends React.Component {
|
|
|
18020
18020
|
widget_id: this.props.id
|
|
18021
18021
|
},
|
|
18022
18022
|
onError: error => {
|
|
18023
|
-
// TODO(LEMS-2826): Remove analytics event in LEMS-2826 in favor of ti below.
|
|
18024
|
-
analytics.onAnalyticsEvent({
|
|
18025
|
-
type: "perseus:widget-rendering-error",
|
|
18026
|
-
payload: {
|
|
18027
|
-
widgetSubType: subType,
|
|
18028
|
-
widgetType: type,
|
|
18029
|
-
widgetId: this.props.id,
|
|
18030
|
-
message: error.message,
|
|
18031
|
-
userAgent: userAgent
|
|
18032
|
-
}
|
|
18033
|
-
});
|
|
18034
18023
|
analytics.onAnalyticsEvent({
|
|
18035
18024
|
type: "perseus:widget-rendering-error:ti",
|
|
18036
18025
|
payload: {
|
|
@@ -27696,7 +27685,8 @@ const defaultGraphConfig = {
|
|
|
27696
27685
|
width: 0,
|
|
27697
27686
|
height: 0,
|
|
27698
27687
|
labels: [],
|
|
27699
|
-
disableKeyboardInteraction: false
|
|
27688
|
+
disableKeyboardInteraction: false,
|
|
27689
|
+
interactiveColor: "var(--mafs-blue)"
|
|
27700
27690
|
};
|
|
27701
27691
|
const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
|
|
27702
27692
|
function useGraphConfig() {
|
|
@@ -27767,6 +27757,11 @@ function Arrowhead(props) {
|
|
|
27767
27757
|
var _props$color;
|
|
27768
27758
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
27769
27759
|
return /*#__PURE__*/React.createElement("g", {
|
|
27760
|
+
// Use aria-hidden to hide the line from screen readers
|
|
27761
|
+
// so it doesn't read as "image" with no context.
|
|
27762
|
+
// The elements using this should have their own aria-labels,
|
|
27763
|
+
// so this is okay.
|
|
27764
|
+
"aria-hidden": true,
|
|
27770
27765
|
className: "interactive-graph-arrowhead",
|
|
27771
27766
|
transform: `translate(${point[X]} ${point[Y]}) rotate(${props.angle})`
|
|
27772
27767
|
}, /*#__PURE__*/React.createElement("g", {
|
|
@@ -28931,6 +28926,11 @@ function SVGLine(props) {
|
|
|
28931
28926
|
testId
|
|
28932
28927
|
} = props;
|
|
28933
28928
|
return /*#__PURE__*/React.createElement("line", {
|
|
28929
|
+
// Use aria-hidden to hide the line from screen readers
|
|
28930
|
+
// so it doesn't read as "image" with no context.
|
|
28931
|
+
// The elements using this should have their own aria-labels,
|
|
28932
|
+
// so this is okay.
|
|
28933
|
+
"aria-hidden": true,
|
|
28934
28934
|
x1: start[X],
|
|
28935
28935
|
y1: start[Y],
|
|
28936
28936
|
x2: end[X],
|
|
@@ -28941,15 +28941,23 @@ function SVGLine(props) {
|
|
|
28941
28941
|
});
|
|
28942
28942
|
}
|
|
28943
28943
|
|
|
28944
|
+
/**
|
|
28945
|
+
* Used to visualize vectors for both interative elements
|
|
28946
|
+
* as well as locked figures within an Interactive Graph widget.
|
|
28947
|
+
*/
|
|
28944
28948
|
const {
|
|
28945
28949
|
calculateAngleInDegrees: calculateAngleInDegrees$3
|
|
28946
28950
|
} = angles;
|
|
28947
28951
|
function Vector(props) {
|
|
28952
|
+
const {
|
|
28953
|
+
interactiveColor
|
|
28954
|
+
} = useGraphConfig();
|
|
28948
28955
|
const {
|
|
28949
28956
|
tail,
|
|
28950
28957
|
tip,
|
|
28951
|
-
color,
|
|
28952
|
-
style
|
|
28958
|
+
color = interactiveColor,
|
|
28959
|
+
style,
|
|
28960
|
+
testId
|
|
28953
28961
|
} = props;
|
|
28954
28962
|
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
28955
28963
|
const direction = vec.sub(tipPx, tailPx);
|
|
@@ -28958,7 +28966,8 @@ function Vector(props) {
|
|
|
28958
28966
|
style: {
|
|
28959
28967
|
stroke: color,
|
|
28960
28968
|
strokeWidth: 2
|
|
28961
|
-
}
|
|
28969
|
+
},
|
|
28970
|
+
"data-testid": testId
|
|
28962
28971
|
}, /*#__PURE__*/React.createElement(SVGLine, {
|
|
28963
28972
|
start: tailPx,
|
|
28964
28973
|
end: tipPx,
|
|
@@ -29253,9 +29262,17 @@ function getAngleFromPoints(points, i) {
|
|
|
29253
29262
|
const a = vec.dist(point, pt1);
|
|
29254
29263
|
const b = vec.dist(point, pt2);
|
|
29255
29264
|
const c = vec.dist(pt1, pt2);
|
|
29265
|
+
let lawOfCosinesRadicand = (a ** 2 + b ** 2 - c ** 2) / (2 * a * b);
|
|
29266
|
+
|
|
29267
|
+
// If the equation results in a number greater than 1 or less than -1.
|
|
29268
|
+
// Correct to ensure a valid angle.
|
|
29269
|
+
// This ensures we are not producing NaN results from Math.acos.
|
|
29270
|
+
if (lawOfCosinesRadicand < -1 || lawOfCosinesRadicand > 1) {
|
|
29271
|
+
lawOfCosinesRadicand = Math.round(lawOfCosinesRadicand);
|
|
29272
|
+
}
|
|
29256
29273
|
|
|
29257
29274
|
// Law of cosines
|
|
29258
|
-
const angle = Math.acos(
|
|
29275
|
+
const angle = Math.acos(lawOfCosinesRadicand);
|
|
29259
29276
|
return angle;
|
|
29260
29277
|
}
|
|
29261
29278
|
function getSideLengthsFromPoints(points, i, isPolygonOpen) {
|
|
@@ -29779,9 +29796,17 @@ const PolygonAngle = ({
|
|
|
29779
29796
|
const a = vec.dist(centerPoint, endPoints[0]);
|
|
29780
29797
|
const b = vec.dist(centerPoint, endPoints[1]);
|
|
29781
29798
|
const c = vec.dist(endPoints[0], endPoints[1]);
|
|
29799
|
+
let lawOfCosinesRadicand = (a ** 2 + b ** 2 - c ** 2) / (2 * a * b);
|
|
29800
|
+
|
|
29801
|
+
// If the equation results in a number greater than 1 or less than -1.
|
|
29802
|
+
// Correct to ensure a valid angle.
|
|
29803
|
+
// This ensures we are not producing NaN results from Math.acos.
|
|
29804
|
+
if (lawOfCosinesRadicand < -1 || lawOfCosinesRadicand > 1) {
|
|
29805
|
+
lawOfCosinesRadicand = Math.round(lawOfCosinesRadicand);
|
|
29806
|
+
}
|
|
29782
29807
|
|
|
29783
29808
|
// Law of cosines
|
|
29784
|
-
const angle = Math.acos(
|
|
29809
|
+
const angle = Math.acos(lawOfCosinesRadicand);
|
|
29785
29810
|
const y1 = centerY + (startY - centerY) / a * radius;
|
|
29786
29811
|
const x2 = centerX + (endX - centerX) / b * radius;
|
|
29787
29812
|
const x1 = centerX + (startX - centerX) / a * radius;
|
|
@@ -29824,7 +29849,7 @@ const PolygonAngle = ({
|
|
|
29824
29849
|
}), /*#__PURE__*/React.createElement("feComposite", {
|
|
29825
29850
|
operator: "over",
|
|
29826
29851
|
in: "SourceGraphic"
|
|
29827
|
-
}))), !isOutside &&
|
|
29852
|
+
}))), !isOutside && isRightPolygonAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
|
|
29828
29853
|
start: [x1, y1],
|
|
29829
29854
|
vertex: [x2, y2],
|
|
29830
29855
|
end: [x3, y3]
|
|
@@ -29897,6 +29922,12 @@ const Angle = ({
|
|
|
29897
29922
|
// Calculate the text position based on the angle and whether we allow reflex angles
|
|
29898
29923
|
// Let's try the angle bisector method to find the midpoint of the arc
|
|
29899
29924
|
const [textX, textY] = calculateBisectorPoint(point1, point2, vertex, isReflexive, allowReflexAngles, radius);
|
|
29925
|
+
|
|
29926
|
+
// Determine the color style for the arc when interactive vs disabled
|
|
29927
|
+
const {
|
|
29928
|
+
disableKeyboardInteraction
|
|
29929
|
+
} = useGraphConfig();
|
|
29930
|
+
const arcClassName = disableKeyboardInteraction ? "angle-arc-static" : "angle-arc-interactive";
|
|
29900
29931
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
|
|
29901
29932
|
id: "background",
|
|
29902
29933
|
x: "-5%",
|
|
@@ -29913,10 +29944,10 @@ const Angle = ({
|
|
|
29913
29944
|
start: [x1, y1],
|
|
29914
29945
|
vertex: [x2, y2],
|
|
29915
29946
|
end: [x3, y3],
|
|
29916
|
-
className:
|
|
29947
|
+
className: arcClassName
|
|
29917
29948
|
}) : /*#__PURE__*/React.createElement(Arc, {
|
|
29918
29949
|
arc: arc,
|
|
29919
|
-
className:
|
|
29950
|
+
className: arcClassName
|
|
29920
29951
|
}), showAngles && /*#__PURE__*/React.createElement(TextLabel, {
|
|
29921
29952
|
x: textX,
|
|
29922
29953
|
y: textY,
|
|
@@ -29934,10 +29965,16 @@ const RightAngleSquare = ({
|
|
|
29934
29965
|
end: [x3, y3],
|
|
29935
29966
|
className
|
|
29936
29967
|
}) => /*#__PURE__*/React.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React.createElement("path", {
|
|
29968
|
+
// Use aria-hidden to hide the line from screen readers
|
|
29969
|
+
// so it doesn't read as "image" with no context.
|
|
29970
|
+
// The elements using this should have their own aria-labels,
|
|
29971
|
+
// so this is okay.
|
|
29972
|
+
"aria-hidden": true,
|
|
29937
29973
|
d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
|
|
29938
29974
|
strokeWidth: 0.02,
|
|
29939
29975
|
fill: "none",
|
|
29940
|
-
className: className
|
|
29976
|
+
className: className,
|
|
29977
|
+
"data-testid": "angle-indicators__right-angle"
|
|
29941
29978
|
}));
|
|
29942
29979
|
|
|
29943
29980
|
// We're conditionally adding the class name here so that we can style the arc differently
|
|
@@ -29947,10 +29984,16 @@ const Arc = ({
|
|
|
29947
29984
|
className
|
|
29948
29985
|
}) => {
|
|
29949
29986
|
return /*#__PURE__*/React.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React.createElement("path", {
|
|
29987
|
+
// Use aria-hidden to hide the line from screen readers
|
|
29988
|
+
// so it doesn't read as "image" with no context.
|
|
29989
|
+
// The elements using this should have their own aria-labels,
|
|
29990
|
+
// so this is okay.
|
|
29991
|
+
"aria-hidden": true,
|
|
29950
29992
|
d: arc,
|
|
29951
29993
|
strokeWidth: 0.02,
|
|
29952
29994
|
fill: "none",
|
|
29953
|
-
className: className
|
|
29995
|
+
className: className,
|
|
29996
|
+
"data-testid": "angle-indicators__arc"
|
|
29954
29997
|
}));
|
|
29955
29998
|
};
|
|
29956
29999
|
const isRightPolygonAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
|
|
@@ -30222,7 +30265,13 @@ function Hairlines(props) {
|
|
|
30222
30265
|
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
30223
30266
|
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
30224
30267
|
const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
|
|
30225
|
-
return /*#__PURE__*/React.createElement("g",
|
|
30268
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
30269
|
+
// Use aria-hidden to hide the line from screen readers
|
|
30270
|
+
// so it doesn't read as "image" with no context.
|
|
30271
|
+
// The elements using this should have their own aria-labels,
|
|
30272
|
+
// so this is okay.
|
|
30273
|
+
"aria-hidden": true
|
|
30274
|
+
}, /*#__PURE__*/React.createElement("line", {
|
|
30226
30275
|
x1: verticalStartX,
|
|
30227
30276
|
y1: y,
|
|
30228
30277
|
x2: verticalEndX,
|
|
@@ -30248,11 +30297,12 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
30248
30297
|
var _Object$entries$find$, _Object$entries$find;
|
|
30249
30298
|
const {
|
|
30250
30299
|
markings,
|
|
30251
|
-
showTooltips
|
|
30300
|
+
showTooltips,
|
|
30301
|
+
interactiveColor
|
|
30252
30302
|
} = useGraphConfig();
|
|
30253
30303
|
const {
|
|
30254
30304
|
point,
|
|
30255
|
-
color: color$1 =
|
|
30305
|
+
color: color$1 = interactiveColor,
|
|
30256
30306
|
dragging,
|
|
30257
30307
|
focused,
|
|
30258
30308
|
cursor,
|
|
@@ -30267,6 +30317,11 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
30267
30317
|
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
30268
30318
|
const showHairlines = (dragging || focused) && markings !== "none";
|
|
30269
30319
|
const svgForPoint = /*#__PURE__*/React.createElement("g", {
|
|
30320
|
+
// Use aria-hidden to hide the line from screen readers
|
|
30321
|
+
// so it doesn't read as "image" with no context.
|
|
30322
|
+
// The elements using this should have their own aria-labels,
|
|
30323
|
+
// so this is okay.
|
|
30324
|
+
"aria-hidden": true,
|
|
30270
30325
|
ref: hitboxRef,
|
|
30271
30326
|
className: pointClasses,
|
|
30272
30327
|
style: {
|
|
@@ -30367,9 +30422,22 @@ function useControlPoint(params) {
|
|
|
30367
30422
|
x: srFormatNumber(point[X], locale),
|
|
30368
30423
|
y: srFormatNumber(point[Y], locale)
|
|
30369
30424
|
});
|
|
30425
|
+
|
|
30426
|
+
// Set the forwarded ref to the focusable handle element when it changes.
|
|
30370
30427
|
useLayoutEffect(() => {
|
|
30371
30428
|
setForwardedRef(forwardedRef, focusableHandleRef.current);
|
|
30372
30429
|
}, [forwardedRef]);
|
|
30430
|
+
|
|
30431
|
+
// If the point is being dragged and is not focused, focus the focusable handle.
|
|
30432
|
+
useLayoutEffect(() => {
|
|
30433
|
+
if (dragging && !focused) {
|
|
30434
|
+
var _focusableHandleRef$c;
|
|
30435
|
+
// If the point is being dragged, focus the focusable handle so that
|
|
30436
|
+
// users can continue to interact with the point using the keyboard or buttons.
|
|
30437
|
+
// This particular focus call ensures that the focus ring and hairlines are visible.
|
|
30438
|
+
(_focusableHandleRef$c = focusableHandleRef.current) == null || _focusableHandleRef$c.focus();
|
|
30439
|
+
}
|
|
30440
|
+
}, [dragging, focused]);
|
|
30373
30441
|
const focusableHandle = /*#__PURE__*/React.createElement("g", {
|
|
30374
30442
|
"data-testid": "movable-point__focusable-handle",
|
|
30375
30443
|
className: "movable-point__focusable-handle",
|
|
@@ -30391,9 +30459,9 @@ function useControlPoint(params) {
|
|
|
30391
30459
|
const visiblePoint = /*#__PURE__*/React.createElement(MovablePointView, {
|
|
30392
30460
|
cursor: cursor,
|
|
30393
30461
|
onClick: () => {
|
|
30394
|
-
var _focusableHandleRef$
|
|
30462
|
+
var _focusableHandleRef$c2;
|
|
30395
30463
|
onClick();
|
|
30396
|
-
(_focusableHandleRef$
|
|
30464
|
+
(_focusableHandleRef$c2 = focusableHandleRef.current) == null || _focusableHandleRef$c2.focus();
|
|
30397
30465
|
},
|
|
30398
30466
|
point: point,
|
|
30399
30467
|
dragging: dragging,
|
|
@@ -30431,7 +30499,6 @@ const MovableLine = props => {
|
|
|
30431
30499
|
points: [start, end],
|
|
30432
30500
|
ariaLabels,
|
|
30433
30501
|
ariaDescribedBy,
|
|
30434
|
-
color,
|
|
30435
30502
|
extend,
|
|
30436
30503
|
onMoveLine = () => {},
|
|
30437
30504
|
onMovePoint = () => {}
|
|
@@ -30466,7 +30533,6 @@ const MovableLine = props => {
|
|
|
30466
30533
|
ariaLive: ariaLives[0],
|
|
30467
30534
|
point: start,
|
|
30468
30535
|
sequenceNumber: 1,
|
|
30469
|
-
color,
|
|
30470
30536
|
onMove: p => {
|
|
30471
30537
|
setAriaLives(["polite", "off", "off"]);
|
|
30472
30538
|
onMovePoint(0, p);
|
|
@@ -30482,7 +30548,6 @@ const MovableLine = props => {
|
|
|
30482
30548
|
ariaLive: ariaLives[1],
|
|
30483
30549
|
point: end,
|
|
30484
30550
|
sequenceNumber: 2,
|
|
30485
|
-
color,
|
|
30486
30551
|
onMove: p => {
|
|
30487
30552
|
setAriaLives(["off", "polite", "off"]);
|
|
30488
30553
|
onMovePoint(1, p);
|
|
@@ -30495,7 +30560,6 @@ const MovableLine = props => {
|
|
|
30495
30560
|
ariaLive: ariaLives[2],
|
|
30496
30561
|
start: start,
|
|
30497
30562
|
end: end,
|
|
30498
|
-
stroke: color,
|
|
30499
30563
|
extend: extend,
|
|
30500
30564
|
onMove: delta => {
|
|
30501
30565
|
setAriaLives(["off", "off", "polite"]);
|
|
@@ -30504,7 +30568,6 @@ const MovableLine = props => {
|
|
|
30504
30568
|
});
|
|
30505
30569
|
return /*#__PURE__*/React.createElement(React.Fragment, null, focusableHandle1, line, focusableHandle2, visiblePoint1, visiblePoint2);
|
|
30506
30570
|
};
|
|
30507
|
-
const defaultStroke = "var(--movable-line-stroke-color)";
|
|
30508
30571
|
const Line$1 = props => {
|
|
30509
30572
|
const {
|
|
30510
30573
|
start,
|
|
@@ -30513,7 +30576,6 @@ const Line$1 = props => {
|
|
|
30513
30576
|
ariaDescribedBy,
|
|
30514
30577
|
ariaLive,
|
|
30515
30578
|
extend,
|
|
30516
|
-
stroke = defaultStroke,
|
|
30517
30579
|
onMove
|
|
30518
30580
|
} = props;
|
|
30519
30581
|
const [startPtPx, endPtPx] = useTransformVectorsToPixels(start, end);
|
|
@@ -30521,7 +30583,8 @@ const Line$1 = props => {
|
|
|
30521
30583
|
range,
|
|
30522
30584
|
graphDimensionsInPixels,
|
|
30523
30585
|
snapStep,
|
|
30524
|
-
disableKeyboardInteraction
|
|
30586
|
+
disableKeyboardInteraction,
|
|
30587
|
+
interactiveColor
|
|
30525
30588
|
} = useGraphConfig();
|
|
30526
30589
|
let startExtend = undefined;
|
|
30527
30590
|
let endExtend = undefined;
|
|
@@ -30578,7 +30641,7 @@ const Line$1 = props => {
|
|
|
30578
30641
|
start: startPtPx,
|
|
30579
30642
|
end: endPtPx,
|
|
30580
30643
|
style: {
|
|
30581
|
-
stroke,
|
|
30644
|
+
stroke: interactiveColor,
|
|
30582
30645
|
strokeWidth: "var(--movable-line-stroke-weight)"
|
|
30583
30646
|
},
|
|
30584
30647
|
className: dragging ? "movable-dragging" : "",
|
|
@@ -30586,11 +30649,11 @@ const Line$1 = props => {
|
|
|
30586
30649
|
})), startExtend && /*#__PURE__*/React.createElement(Vector, {
|
|
30587
30650
|
tail: start,
|
|
30588
30651
|
tip: startExtend,
|
|
30589
|
-
|
|
30652
|
+
testId: "movable-line__vector"
|
|
30590
30653
|
}), endExtend && /*#__PURE__*/React.createElement(Vector, {
|
|
30591
30654
|
tail: end,
|
|
30592
30655
|
tip: endExtend,
|
|
30593
|
-
|
|
30656
|
+
testId: "movable-line__vector"
|
|
30594
30657
|
}));
|
|
30595
30658
|
};
|
|
30596
30659
|
const getMovableLineKeyboardConstraint = (line, snapStep, pointIndex) => {
|
|
@@ -30662,7 +30725,8 @@ function AngleGraph(props) {
|
|
|
30662
30725
|
graphState
|
|
30663
30726
|
} = props;
|
|
30664
30727
|
const {
|
|
30665
|
-
graphDimensionsInPixels
|
|
30728
|
+
graphDimensionsInPixels,
|
|
30729
|
+
interactiveColor
|
|
30666
30730
|
} = useGraphConfig();
|
|
30667
30731
|
const i18n = usePerseusI18n();
|
|
30668
30732
|
const id = React.useId();
|
|
@@ -30693,13 +30757,14 @@ function AngleGraph(props) {
|
|
|
30693
30757
|
start: startPtPx,
|
|
30694
30758
|
end: endPtPx,
|
|
30695
30759
|
style: {
|
|
30696
|
-
stroke:
|
|
30760
|
+
stroke: interactiveColor,
|
|
30697
30761
|
strokeWidth: "var(--movable-line-stroke-weight)"
|
|
30698
|
-
}
|
|
30762
|
+
},
|
|
30763
|
+
testId: "angle-graph__line"
|
|
30699
30764
|
}), /*#__PURE__*/React.createElement(Vector, {
|
|
30700
30765
|
tail: angleLines[i][1],
|
|
30701
30766
|
tip: endExtend,
|
|
30702
|
-
|
|
30767
|
+
testId: "angle-graph__vector"
|
|
30703
30768
|
}));
|
|
30704
30769
|
});
|
|
30705
30770
|
|
|
@@ -31043,7 +31108,8 @@ function MovableCircle(props) {
|
|
|
31043
31108
|
} = props;
|
|
31044
31109
|
const {
|
|
31045
31110
|
snapStep,
|
|
31046
|
-
disableKeyboardInteraction
|
|
31111
|
+
disableKeyboardInteraction,
|
|
31112
|
+
interactiveColor
|
|
31047
31113
|
} = useGraphConfig();
|
|
31048
31114
|
const [focused, setFocused] = React.useState(false);
|
|
31049
31115
|
const draggableRef = useRef(null);
|
|
@@ -31079,7 +31145,9 @@ function MovableCircle(props) {
|
|
|
31079
31145
|
cx: centerPx[X],
|
|
31080
31146
|
cy: centerPx[Y],
|
|
31081
31147
|
rx: radiiPx[X],
|
|
31082
|
-
ry: radiiPx[Y]
|
|
31148
|
+
ry: radiiPx[Y],
|
|
31149
|
+
stroke: interactiveColor,
|
|
31150
|
+
"data-testid": "movable-circle__circle"
|
|
31083
31151
|
}), /*#__PURE__*/React.createElement(DragHandle, {
|
|
31084
31152
|
center: center,
|
|
31085
31153
|
dragging: dragging,
|
|
@@ -31096,7 +31164,8 @@ function DragHandle(props) {
|
|
|
31096
31164
|
} = props;
|
|
31097
31165
|
const [centerPx] = useTransformVectorsToPixels(center);
|
|
31098
31166
|
const {
|
|
31099
|
-
markings
|
|
31167
|
+
markings,
|
|
31168
|
+
interactiveColor
|
|
31100
31169
|
} = useGraphConfig();
|
|
31101
31170
|
const cornerRadius = Math.min(...dragHandleDimensions) / 2;
|
|
31102
31171
|
const topLeft = vec.sub(centerPx, vec.scale(dragHandleDimensions, 0.5));
|
|
@@ -31110,7 +31179,9 @@ function DragHandle(props) {
|
|
|
31110
31179
|
width: dragHandleDimensions[X],
|
|
31111
31180
|
height: dragHandleDimensions[Y],
|
|
31112
31181
|
rx: cornerRadius,
|
|
31113
|
-
ry: cornerRadius
|
|
31182
|
+
ry: cornerRadius,
|
|
31183
|
+
fill: interactiveColor,
|
|
31184
|
+
"data-testid": "movable-circle__handle"
|
|
31114
31185
|
}), dragHandleDotPositions.map(offsetPx => {
|
|
31115
31186
|
const [xPx, yPx] = vec.add(offsetPx, centerPx);
|
|
31116
31187
|
return /*#__PURE__*/React.createElement("circle", {
|
|
@@ -31269,8 +31340,7 @@ const LinearGraph = (props, key) => {
|
|
|
31269
31340
|
start: true,
|
|
31270
31341
|
end: true
|
|
31271
31342
|
},
|
|
31272
|
-
onMovePoint: (endpointIndex, destination) => dispatch(actions.linear.movePoint(endpointIndex, destination))
|
|
31273
|
-
color: "var(--movable-line-stroke-color)"
|
|
31343
|
+
onMovePoint: (endpointIndex, destination) => dispatch(actions.linear.movePoint(endpointIndex, destination))
|
|
31274
31344
|
}), /*#__PURE__*/React.createElement("g", {
|
|
31275
31345
|
id: pointsDescriptionId,
|
|
31276
31346
|
style: a11y.srOnly
|
|
@@ -31409,8 +31479,7 @@ const LinearSystemGraph = props => {
|
|
|
31409
31479
|
start: true,
|
|
31410
31480
|
end: true
|
|
31411
31481
|
},
|
|
31412
|
-
onMovePoint: (endpointIndex, destination) => dispatch(actions.linearSystem.movePointInFigure(i, endpointIndex, destination))
|
|
31413
|
-
color: "var(--movable-line-stroke-color)"
|
|
31482
|
+
onMovePoint: (endpointIndex, destination) => dispatch(actions.linearSystem.movePointInFigure(i, endpointIndex, destination))
|
|
31414
31483
|
})), linesAriaInfo.map(({
|
|
31415
31484
|
pointsDescriptionId,
|
|
31416
31485
|
interceptDescriptionId,
|
|
@@ -32226,6 +32295,7 @@ function doMovePoint(state, action) {
|
|
|
32226
32295
|
{
|
|
32227
32296
|
return _extends({}, state, {
|
|
32228
32297
|
hasBeenInteractedWith: true,
|
|
32298
|
+
focusedPointIndex: action.index,
|
|
32229
32299
|
coords: setAtIndex({
|
|
32230
32300
|
array: state.coords,
|
|
32231
32301
|
index: action.index,
|
|
@@ -32545,7 +32615,9 @@ function boundAndSnapToPolygonAngle(destinationPoint, {
|
|
|
32545
32615
|
coords
|
|
32546
32616
|
}, index) {
|
|
32547
32617
|
const startingPoint = coords[index];
|
|
32548
|
-
|
|
32618
|
+
return calculateAngleSnap(destinationPoint, range, coords, index, startingPoint);
|
|
32619
|
+
}
|
|
32620
|
+
function calculateAngleSnap(destinationPoint, range, coords, index, startingPoint) {
|
|
32549
32621
|
// Needed to prevent updating the original coords before the checks for
|
|
32550
32622
|
// degenerate triangles and overlapping sides
|
|
32551
32623
|
const coordsCopy = [...coords];
|
|
@@ -32834,7 +32906,8 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32834
32906
|
snapStep
|
|
32835
32907
|
} = statefulProps.graphState;
|
|
32836
32908
|
const {
|
|
32837
|
-
disableKeyboardInteraction
|
|
32909
|
+
disableKeyboardInteraction,
|
|
32910
|
+
interactiveColor
|
|
32838
32911
|
} = graphConfig;
|
|
32839
32912
|
const {
|
|
32840
32913
|
strings,
|
|
@@ -32870,12 +32943,16 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32870
32943
|
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
32871
32944
|
}, /*#__PURE__*/React.createElement(Polygon, {
|
|
32872
32945
|
points: [...points],
|
|
32873
|
-
color:
|
|
32946
|
+
color: interactiveColor,
|
|
32874
32947
|
svgPolygonProps: {
|
|
32875
32948
|
strokeWidth: focusVisible ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)",
|
|
32876
32949
|
style: {
|
|
32877
32950
|
fill: "transparent"
|
|
32878
|
-
}
|
|
32951
|
+
},
|
|
32952
|
+
// Use aria-hidden to hide the line from screen readers
|
|
32953
|
+
// so it doesn't read as "image" with no context.
|
|
32954
|
+
// This is okay because the graph has its own aria-label.
|
|
32955
|
+
"aria-hidden": true
|
|
32879
32956
|
}
|
|
32880
32957
|
}), points.map((point, i) => {
|
|
32881
32958
|
const pt1 = points.at(i - 1);
|
|
@@ -33015,6 +33092,9 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
33015
33092
|
strings,
|
|
33016
33093
|
locale
|
|
33017
33094
|
} = usePerseusI18n();
|
|
33095
|
+
const {
|
|
33096
|
+
interactiveColor
|
|
33097
|
+
} = useGraphConfig();
|
|
33018
33098
|
const id = React.useId();
|
|
33019
33099
|
const polygonPointsNumId = id + "-points-num";
|
|
33020
33100
|
const polygonPointsId = id + "-points";
|
|
@@ -33054,14 +33134,22 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
33054
33134
|
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
33055
33135
|
}, /*#__PURE__*/React.createElement(Polyline, {
|
|
33056
33136
|
points: [...points],
|
|
33057
|
-
color:
|
|
33137
|
+
color: interactiveColor,
|
|
33058
33138
|
svgPolylineProps: {
|
|
33059
33139
|
strokeWidth: "var(--movable-line-stroke-weight)",
|
|
33060
33140
|
style: {
|
|
33061
33141
|
fill: "transparent"
|
|
33062
|
-
}
|
|
33142
|
+
},
|
|
33143
|
+
// Use aria-hidden to hide the line from screen readers
|
|
33144
|
+
// so it doesn't read as "image" with no context.
|
|
33145
|
+
// This is okay because the graph has its own aria-label.
|
|
33146
|
+
"aria-hidden": true
|
|
33063
33147
|
}
|
|
33064
33148
|
}), /*#__PURE__*/React.createElement("rect", {
|
|
33149
|
+
// Use aria-hidden to hide the line from screen readers
|
|
33150
|
+
// so it doesn't read as "image" with no context.
|
|
33151
|
+
// This is okay because the graph has its own aria-label.
|
|
33152
|
+
"aria-hidden": true,
|
|
33065
33153
|
style: {
|
|
33066
33154
|
fill: "rgba(0,0,0,0)",
|
|
33067
33155
|
cursor: "crosshair"
|
|
@@ -33215,7 +33303,7 @@ function getKeyboardMovementConstraintForPoint(points, index, range, snapStep, s
|
|
|
33215
33303
|
case "sides":
|
|
33216
33304
|
return getSideSnapConstraint(points, index, range);
|
|
33217
33305
|
case "angles":
|
|
33218
|
-
return
|
|
33306
|
+
return getAngleSnapConstraint(points, index, range);
|
|
33219
33307
|
default:
|
|
33220
33308
|
throw new UnreachableCaseError(snapTo);
|
|
33221
33309
|
}
|
|
@@ -33269,6 +33357,49 @@ function getSideSnapConstraint(points, index, range) {
|
|
|
33269
33357
|
right: movePointWithConstraint(coord => vec.add(coord, [1, 0]))
|
|
33270
33358
|
};
|
|
33271
33359
|
}
|
|
33360
|
+
function getAngleSnapConstraint(points, index, range) {
|
|
33361
|
+
// Make newPoints mutable.
|
|
33362
|
+
const newPoints = [...points];
|
|
33363
|
+
|
|
33364
|
+
// Get the point that is being moved.
|
|
33365
|
+
const pointToBeMoved = newPoints[index];
|
|
33366
|
+
|
|
33367
|
+
// Create a helper function that moves the point to a valid location
|
|
33368
|
+
// for angle snapping.
|
|
33369
|
+
const movePointWithConstraint = moveFunc => {
|
|
33370
|
+
// The direction the user is attempting to move the point in.
|
|
33371
|
+
let destinationAttempt = bound$1({
|
|
33372
|
+
snapStep: [0, 0],
|
|
33373
|
+
range,
|
|
33374
|
+
point: moveFunc(pointToBeMoved)
|
|
33375
|
+
});
|
|
33376
|
+
// The new point we're moving to.
|
|
33377
|
+
let newPoint = pointToBeMoved;
|
|
33378
|
+
|
|
33379
|
+
// Move the point and keep trying until we are at the boarder
|
|
33380
|
+
// of the graph.
|
|
33381
|
+
while (newPoint[0] === pointToBeMoved[0] && newPoint[1] === pointToBeMoved[1] && isInBound({
|
|
33382
|
+
range,
|
|
33383
|
+
point: destinationAttempt
|
|
33384
|
+
})) {
|
|
33385
|
+
newPoint = calculateAngleSnap(destinationAttempt, range, newPoints, index, pointToBeMoved);
|
|
33386
|
+
|
|
33387
|
+
// Increment the destinationAttempt.
|
|
33388
|
+
// For every time it does not work increment the direction for x and y.
|
|
33389
|
+
destinationAttempt = moveFunc(destinationAttempt);
|
|
33390
|
+
}
|
|
33391
|
+
return newPoint;
|
|
33392
|
+
};
|
|
33393
|
+
|
|
33394
|
+
// For each direction look for the next movable point by a small step to increase changes
|
|
33395
|
+
// of finding the next angle value.
|
|
33396
|
+
return {
|
|
33397
|
+
up: movePointWithConstraint(coord => vec.add(coord, [0, 0.1])),
|
|
33398
|
+
down: movePointWithConstraint(coord => vec.sub(coord, [0, 0.1])),
|
|
33399
|
+
left: movePointWithConstraint(coord => vec.sub(coord, [0.1, 0])),
|
|
33400
|
+
right: movePointWithConstraint(coord => vec.add(coord, [0.1, 0]))
|
|
33401
|
+
};
|
|
33402
|
+
}
|
|
33272
33403
|
|
|
33273
33404
|
function renderQuadraticGraph(state, dispatch, i18n) {
|
|
33274
33405
|
return {
|
|
@@ -33288,6 +33419,9 @@ function QuadraticGraph(props) {
|
|
|
33288
33419
|
coords,
|
|
33289
33420
|
snapStep
|
|
33290
33421
|
} = graphState;
|
|
33422
|
+
const {
|
|
33423
|
+
interactiveColor
|
|
33424
|
+
} = useGraphConfig();
|
|
33291
33425
|
const {
|
|
33292
33426
|
strings,
|
|
33293
33427
|
locale
|
|
@@ -33329,7 +33463,13 @@ function QuadraticGraph(props) {
|
|
|
33329
33463
|
"aria-describedby": `${quadraticDirectionId} ${quadraticVertexId} ${quadraticInterceptsId}`
|
|
33330
33464
|
}, /*#__PURE__*/React.createElement(Plot$2.OfX, {
|
|
33331
33465
|
y: y,
|
|
33332
|
-
color:
|
|
33466
|
+
color: interactiveColor,
|
|
33467
|
+
svgPathProps: {
|
|
33468
|
+
// Use aria-hidden to hide the line from screen readers
|
|
33469
|
+
// so it doesn't read as "image" with no context.
|
|
33470
|
+
// This is okay because the graph has its own aria-label.
|
|
33471
|
+
"aria-hidden": true
|
|
33472
|
+
}
|
|
33333
33473
|
}), coords.map((coord, i) => {
|
|
33334
33474
|
const srQuadraticPoint = getQuadraticPointString(i + 1, coord, strings, locale);
|
|
33335
33475
|
// Vertex might not exist if the quadratic graph is a line
|
|
@@ -33724,6 +33864,9 @@ function SinusoidGraph(props) {
|
|
|
33724
33864
|
dispatch,
|
|
33725
33865
|
graphState
|
|
33726
33866
|
} = props;
|
|
33867
|
+
const {
|
|
33868
|
+
interactiveColor
|
|
33869
|
+
} = useGraphConfig();
|
|
33727
33870
|
const i18n = usePerseusI18n();
|
|
33728
33871
|
const id = React.useId();
|
|
33729
33872
|
const descriptionId = id + "-description";
|
|
@@ -33766,7 +33909,13 @@ function SinusoidGraph(props) {
|
|
|
33766
33909
|
"aria-describedby": descriptionId
|
|
33767
33910
|
}, /*#__PURE__*/React.createElement(Plot$2.OfX, {
|
|
33768
33911
|
y: x => computeSine(x, coeffRef.current),
|
|
33769
|
-
color:
|
|
33912
|
+
color: interactiveColor,
|
|
33913
|
+
svgPathProps: {
|
|
33914
|
+
// Use aria-hidden to hide the line from screen readers
|
|
33915
|
+
// so it doesn't read as "image" with no context.
|
|
33916
|
+
// This is okay because the graph has its own aria-label.
|
|
33917
|
+
"aria-hidden": true
|
|
33918
|
+
}
|
|
33770
33919
|
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
33771
33920
|
ariaLabel: i === 0 ? srSinusoidRootPoint : srSinusoidPeakPoint,
|
|
33772
33921
|
key: "point-" + i,
|
|
@@ -34115,6 +34264,7 @@ const MafsGraph = props => {
|
|
|
34115
34264
|
i18n,
|
|
34116
34265
|
markings: props.markings
|
|
34117
34266
|
});
|
|
34267
|
+
const disableInteraction = readOnly || !!props.static;
|
|
34118
34268
|
return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
|
|
34119
34269
|
value: {
|
|
34120
34270
|
range: state.range,
|
|
@@ -34127,7 +34277,12 @@ const MafsGraph = props => {
|
|
|
34127
34277
|
width,
|
|
34128
34278
|
height,
|
|
34129
34279
|
labels,
|
|
34130
|
-
disableKeyboardInteraction:
|
|
34280
|
+
disableKeyboardInteraction: disableInteraction,
|
|
34281
|
+
// If the graph is read-only or static, we want to make it
|
|
34282
|
+
// visually clear that the graph is no longer interactive.
|
|
34283
|
+
// We do this by changing the color of the interactive elements
|
|
34284
|
+
// to a static gray color rather than our standard blue.
|
|
34285
|
+
interactiveColor: disableInteraction ? "var(--static-gray)" : "var(--mafs-blue)"
|
|
34131
34286
|
}
|
|
34132
34287
|
}, /*#__PURE__*/React.createElement(View, {
|
|
34133
34288
|
className: "mafs-graph-container"
|
|
@@ -35143,7 +35298,8 @@ InteractiveGraph.defaultProps = {
|
|
|
35143
35298
|
const staticTransform$3 = _.identity;
|
|
35144
35299
|
var InteractiveGraph$1 = {
|
|
35145
35300
|
name: "interactive-graph",
|
|
35146
|
-
displayName: "Interactive graph
|
|
35301
|
+
displayName: "Interactive graph",
|
|
35302
|
+
accessible: true,
|
|
35147
35303
|
widget: InteractiveGraph,
|
|
35148
35304
|
staticTransform: staticTransform$3
|
|
35149
35305
|
};
|
|
@@ -41232,7 +41388,7 @@ const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
|
|
|
41232
41388
|
|
|
41233
41389
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
41234
41390
|
const libName = "@khanacademy/perseus";
|
|
41235
|
-
const libVersion = "56.
|
|
41391
|
+
const libVersion = "56.2.0";
|
|
41236
41392
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
41237
41393
|
|
|
41238
41394
|
const apiVersion = {
|