@khanacademy/perseus 47.0.1 → 48.0.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.js +221 -180
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +211 -180
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +0 -4
- package/dist/server-item-renderer.d.ts +0 -6
- package/dist/util/test-utils.d.ts +7 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -2412,9 +2412,6 @@ function scoreExpression(userInput, rubric, strings, locale) {
|
|
|
2412
2412
|
// We matched a graded answer form, so we can now tell the user
|
|
2413
2413
|
// whether their input was correct or incorrect, and hand out
|
|
2414
2414
|
// points accordingly
|
|
2415
|
-
// TODO(eater): Seems silly to translate result to this
|
|
2416
|
-
// invalid/points thing and immediately translate it back in
|
|
2417
|
-
// ItemRenderer.scoreInput()
|
|
2418
2415
|
return {
|
|
2419
2416
|
type: "points",
|
|
2420
2417
|
earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
|
|
@@ -3363,7 +3360,7 @@ const supportsPassiveEvents = () => {
|
|
|
3363
3360
|
window.addEventListener("testPassive", null, opts);
|
|
3364
3361
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
3365
3362
|
window.removeEventListener("testPassive", null, opts);
|
|
3366
|
-
} catch
|
|
3363
|
+
} catch {
|
|
3367
3364
|
// Intentionally left empty!
|
|
3368
3365
|
}
|
|
3369
3366
|
return supportsPassive;
|
|
@@ -3604,7 +3601,9 @@ function changeViewportTag(contentString, callback) {
|
|
|
3604
3601
|
// finished resetting.
|
|
3605
3602
|
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
3606
3603
|
// eslint-disable-next-line no-restricted-syntax
|
|
3607
|
-
|
|
3604
|
+
if (callback != null) {
|
|
3605
|
+
setTimeout(callback, 0);
|
|
3606
|
+
}
|
|
3608
3607
|
}, 0);
|
|
3609
3608
|
}
|
|
3610
3609
|
|
|
@@ -16964,7 +16963,7 @@ class ImageLoader extends React__namespace.Component {
|
|
|
16964
16963
|
onKeyUp = e => {
|
|
16965
16964
|
// 13 is enter key, 32 is space key
|
|
16966
16965
|
if (e.keyCode === 13 || e.keyCode === 32) {
|
|
16967
|
-
imgProps.onClick
|
|
16966
|
+
imgProps.onClick?.(e);
|
|
16968
16967
|
}
|
|
16969
16968
|
};
|
|
16970
16969
|
onKeyDown = e => {
|
|
@@ -17033,7 +17032,7 @@ const doJSONP = function (url, options) {
|
|
|
17033
17032
|
|
|
17034
17033
|
// A cleanup function to run when we're done.
|
|
17035
17034
|
function cleanup() {
|
|
17036
|
-
document.head
|
|
17035
|
+
document.head?.removeChild(script);
|
|
17037
17036
|
delete window[options.callbackName];
|
|
17038
17037
|
}
|
|
17039
17038
|
|
|
@@ -17057,7 +17056,7 @@ const doJSONP = function (url, options) {
|
|
|
17057
17056
|
});
|
|
17058
17057
|
|
|
17059
17058
|
// Insert the script to start the download.
|
|
17060
|
-
document.head
|
|
17059
|
+
document.head?.appendChild(script);
|
|
17061
17060
|
};
|
|
17062
17061
|
|
|
17063
17062
|
// For offline exercises in the mobile app, we download the graphie data
|
|
@@ -17396,7 +17395,7 @@ class SvgImage extends React__namespace.Component {
|
|
|
17396
17395
|
$image.width() < this.props.width || this.props.zoomToFullSizeOnMobile) {
|
|
17397
17396
|
ZoomService.handleZoomClick(e, this.props.zoomToFullSizeOnMobile);
|
|
17398
17397
|
}
|
|
17399
|
-
this.props.trackInteraction
|
|
17398
|
+
this.props.trackInteraction?.();
|
|
17400
17399
|
};
|
|
17401
17400
|
handleUpdate = status => {
|
|
17402
17401
|
this.props.onUpdate();
|
|
@@ -19803,8 +19802,8 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
19803
19802
|
};
|
|
19804
19803
|
|
|
19805
19804
|
// Note: if you add more props here, please consider whether or not
|
|
19806
|
-
// it should be auto-serialized
|
|
19807
|
-
// widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
|
|
19805
|
+
// it should be auto-serialized.
|
|
19806
|
+
// See widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
|
|
19808
19807
|
|
|
19809
19808
|
// We default to an empty object for style instead of null
|
|
19810
19809
|
// because of a strange bug where the static styles aren't applied
|
|
@@ -21100,15 +21099,6 @@ class Renderer extends React__namespace.Component {
|
|
|
21100
21099
|
return combinedScore;
|
|
21101
21100
|
}
|
|
21102
21101
|
|
|
21103
|
-
/**
|
|
21104
|
-
* @deprecated use scorePerseusItem
|
|
21105
|
-
*/
|
|
21106
|
-
guessAndScore = () => {
|
|
21107
|
-
const totalGuess = this.getUserInput();
|
|
21108
|
-
const totalScore = this.score();
|
|
21109
|
-
return [totalGuess, totalScore];
|
|
21110
|
-
};
|
|
21111
|
-
|
|
21112
21102
|
// TranslationLinter callback
|
|
21113
21103
|
handletranslationLintErrors = lintErrors => {
|
|
21114
21104
|
if (!this._isMounted) {
|
|
@@ -21786,11 +21776,11 @@ class SimpleKeypadInput extends React__namespace.Component {
|
|
|
21786
21776
|
keypadType: mathInput.KeypadType.FRACTION
|
|
21787
21777
|
}, () => {
|
|
21788
21778
|
if (_this._isMounted) {
|
|
21789
|
-
onFocus
|
|
21779
|
+
onFocus?.();
|
|
21790
21780
|
}
|
|
21791
21781
|
});
|
|
21792
21782
|
} else {
|
|
21793
|
-
onFocus
|
|
21783
|
+
onFocus?.();
|
|
21794
21784
|
}
|
|
21795
21785
|
},
|
|
21796
21786
|
value: value == null ? "" : "" + value
|
|
@@ -22029,9 +22019,6 @@ function scoreInputNumber(userInput, rubric, strings) {
|
|
|
22029
22019
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
22030
22020
|
const currentValue = ParseTex$1(userInput.currentValue);
|
|
22031
22021
|
const result = val(currentValue);
|
|
22032
|
-
|
|
22033
|
-
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
22034
|
-
// thing and immediately translate it back in ItemRenderer.scoreInput()
|
|
22035
22022
|
if (result.empty) {
|
|
22036
22023
|
return {
|
|
22037
22024
|
type: "invalid",
|
|
@@ -22395,10 +22382,6 @@ function scoreNumericInput(userInput, rubric, strings) {
|
|
|
22395
22382
|
message: matchedAnswer?.message ?? null,
|
|
22396
22383
|
guess: localValue
|
|
22397
22384
|
};
|
|
22398
|
-
|
|
22399
|
-
// TODO(eater): Seems silly to translate result to this
|
|
22400
|
-
// invalid/points thing and immediately translate it
|
|
22401
|
-
// back in ItemRenderer.scoreInput()
|
|
22402
22385
|
if (result.empty) {
|
|
22403
22386
|
return {
|
|
22404
22387
|
type: "invalid",
|
|
@@ -25255,7 +25238,7 @@ const scrollElementToOffset = function (elementOrWindow, offset) {
|
|
|
25255
25238
|
// TODO(jeff, WEB-1378): Use Wonder Blocks Timing API
|
|
25256
25239
|
// eslint-disable-next-line no-restricted-syntax
|
|
25257
25240
|
clearInterval(scrollInterval);
|
|
25258
|
-
callback
|
|
25241
|
+
callback?.();
|
|
25259
25242
|
}
|
|
25260
25243
|
}, tickLength);
|
|
25261
25244
|
};
|
|
@@ -27317,7 +27300,7 @@ class CSProgram extends React__namespace.Component {
|
|
|
27317
27300
|
let data = {};
|
|
27318
27301
|
try {
|
|
27319
27302
|
data = JSON.parse(e.originalEvent.data);
|
|
27320
|
-
} catch
|
|
27303
|
+
} catch {
|
|
27321
27304
|
return;
|
|
27322
27305
|
}
|
|
27323
27306
|
if (___default["default"].isUndefined(data.testsPassed)) {
|
|
@@ -28344,7 +28327,6 @@ class GradedGroupSet extends React__namespace.Component {
|
|
|
28344
28327
|
currentGroup: 0
|
|
28345
28328
|
};
|
|
28346
28329
|
shouldComponentUpdate(nextProps, nextState) {
|
|
28347
|
-
nextProps.gradedGroups;
|
|
28348
28330
|
return nextProps !== this.props || nextState !== this.state;
|
|
28349
28331
|
}
|
|
28350
28332
|
change = (() => {
|
|
@@ -29868,7 +29850,7 @@ class Iframe extends React__namespace.Component {
|
|
|
29868
29850
|
let data = {};
|
|
29869
29851
|
try {
|
|
29870
29852
|
data = JSON.parse(e.originalEvent.data);
|
|
29871
|
-
} catch
|
|
29853
|
+
} catch {
|
|
29872
29854
|
return;
|
|
29873
29855
|
}
|
|
29874
29856
|
if (___default["default"].isUndefined(data.testsPassed)) {
|
|
@@ -30963,10 +30945,10 @@ class Graph extends React__namespace.Component {
|
|
|
30963
30945
|
this.props.onMouseDown(coord);
|
|
30964
30946
|
}, this) : undefined;
|
|
30965
30947
|
const onMouseOver = $instructionsWrapper ? function () {
|
|
30966
|
-
$instructionsWrapper
|
|
30948
|
+
$instructionsWrapper?.css("opacity", invisible);
|
|
30967
30949
|
} : undefined;
|
|
30968
30950
|
const onMouseOut = $instructionsWrapper ? function () {
|
|
30969
|
-
$instructionsWrapper
|
|
30951
|
+
$instructionsWrapper?.css("opacity", visible);
|
|
30970
30952
|
} : undefined;
|
|
30971
30953
|
/* eslint-enable indent */
|
|
30972
30954
|
|
|
@@ -31679,6 +31661,7 @@ function replaceOutsideTeX(mathString) {
|
|
|
31679
31661
|
const parsed = mathOnlyParser(mathString);
|
|
31680
31662
|
let result = "";
|
|
31681
31663
|
for (const piece of parsed) {
|
|
31664
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
31682
31665
|
piece.type === "math" ? result += "$" + piece.content + "$" : piece.type === "specialCharacter" ? result += escapeIfUnescaped(piece.content) : result += piece.content;
|
|
31683
31666
|
}
|
|
31684
31667
|
return `\\text{${result}}`;
|
|
@@ -34650,42 +34633,64 @@ function renderPointGraph(state, dispatch) {
|
|
|
34650
34633
|
};
|
|
34651
34634
|
}
|
|
34652
34635
|
function PointGraph(props) {
|
|
34653
|
-
const
|
|
34636
|
+
const {
|
|
34637
|
+
numPoints
|
|
34638
|
+
} = props.graphState;
|
|
34639
|
+
const graphConfig = useGraphConfig();
|
|
34640
|
+
const pointsRef = React__namespace.useRef([]);
|
|
34641
|
+
|
|
34642
|
+
// Dimensions to build the graph overlay for Unlimited Point.
|
|
34643
|
+
const {
|
|
34644
|
+
range: [x, y]
|
|
34645
|
+
} = graphConfig;
|
|
34646
|
+
const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
|
|
34647
|
+
|
|
34648
|
+
// This useEffect is to handle the focus snapping for Unlimited Point.
|
|
34649
|
+
React__namespace.useEffect(() => {
|
|
34650
|
+
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34651
|
+
if (focusedIndex != null) {
|
|
34652
|
+
pointsRef.current[focusedIndex]?.focus();
|
|
34653
|
+
}
|
|
34654
|
+
}, [props.graphState.focusedPointIndex, pointsRef]);
|
|
34655
|
+
const statefulProps = {
|
|
34656
|
+
...props,
|
|
34657
|
+
graphConfig,
|
|
34658
|
+
pointsRef,
|
|
34659
|
+
top,
|
|
34660
|
+
left
|
|
34661
|
+
};
|
|
34654
34662
|
if (numPoints === "unlimited") {
|
|
34655
|
-
return UnlimitedPointGraph(
|
|
34663
|
+
return UnlimitedPointGraph(statefulProps);
|
|
34656
34664
|
}
|
|
34657
|
-
return LimitedPointGraph(
|
|
34665
|
+
return LimitedPointGraph(statefulProps);
|
|
34658
34666
|
}
|
|
34659
|
-
function LimitedPointGraph(
|
|
34667
|
+
function LimitedPointGraph(statefulProps) {
|
|
34660
34668
|
const {
|
|
34661
34669
|
dispatch
|
|
34662
|
-
} =
|
|
34663
|
-
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null,
|
|
34670
|
+
} = statefulProps;
|
|
34671
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, statefulProps.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34664
34672
|
key: i,
|
|
34665
34673
|
point: point,
|
|
34666
34674
|
sequenceNumber: i + 1,
|
|
34667
34675
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
|
|
34668
34676
|
})));
|
|
34669
34677
|
}
|
|
34670
|
-
function UnlimitedPointGraph(
|
|
34678
|
+
function UnlimitedPointGraph(statefulProps) {
|
|
34671
34679
|
const {
|
|
34672
|
-
dispatch
|
|
34673
|
-
|
|
34674
|
-
|
|
34680
|
+
dispatch,
|
|
34681
|
+
graphConfig,
|
|
34682
|
+
pointsRef,
|
|
34683
|
+
top,
|
|
34684
|
+
left
|
|
34685
|
+
} = statefulProps;
|
|
34675
34686
|
const {
|
|
34676
|
-
|
|
34687
|
+
coords
|
|
34688
|
+
} = statefulProps.graphState;
|
|
34689
|
+
const {
|
|
34690
|
+
graphDimensionsInPixels
|
|
34677
34691
|
} = graphConfig;
|
|
34678
|
-
const
|
|
34679
|
-
const
|
|
34680
|
-
const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
|
|
34681
|
-
const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
|
|
34682
|
-
const itemsRef = React__namespace.useRef([]);
|
|
34683
|
-
React__namespace.useEffect(() => {
|
|
34684
|
-
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34685
|
-
if (focusedIndex != null) {
|
|
34686
|
-
itemsRef.current[focusedIndex]?.focus();
|
|
34687
|
-
}
|
|
34688
|
-
}, [props.graphState.focusedPointIndex, itemsRef]);
|
|
34692
|
+
const widthPx = graphDimensionsInPixels[0];
|
|
34693
|
+
const heightPx = graphDimensionsInPixels[1];
|
|
34689
34694
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
|
|
34690
34695
|
style: {
|
|
34691
34696
|
fill: "rgba(0,0,0,0)",
|
|
@@ -34702,13 +34707,13 @@ function UnlimitedPointGraph(props) {
|
|
|
34702
34707
|
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34703
34708
|
dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
|
|
34704
34709
|
}
|
|
34705
|
-
}),
|
|
34710
|
+
}), coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34706
34711
|
key: i,
|
|
34707
34712
|
point: point,
|
|
34708
34713
|
sequenceNumber: i + 1,
|
|
34709
34714
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
|
|
34710
34715
|
ref: ref => {
|
|
34711
|
-
|
|
34716
|
+
pointsRef.current[i] = ref;
|
|
34712
34717
|
},
|
|
34713
34718
|
onFocus: () => {
|
|
34714
34719
|
dispatch(actions.pointGraph.focusPoint(i));
|
|
@@ -34759,36 +34764,42 @@ function renderPolygonGraph(state, dispatch) {
|
|
|
34759
34764
|
interactiveElementsDescription: null
|
|
34760
34765
|
};
|
|
34761
34766
|
}
|
|
34762
|
-
const
|
|
34763
|
-
const [hovered, setHovered] = React__namespace.useState(false);
|
|
34764
|
-
// This is more so required for the re-rendering that occurs when state
|
|
34765
|
-
// updates; specifically with regard to line weighting and polygon focus.
|
|
34766
|
-
const [focusVisible, setFocusVisible] = React__namespace.useState(false);
|
|
34767
|
+
const PolygonGraph = props => {
|
|
34767
34768
|
const {
|
|
34768
34769
|
dispatch
|
|
34769
34770
|
} = props;
|
|
34770
34771
|
const {
|
|
34772
|
+
numSides,
|
|
34771
34773
|
coords,
|
|
34772
|
-
showAngles,
|
|
34773
|
-
showSides,
|
|
34774
|
-
range,
|
|
34775
34774
|
snapStep,
|
|
34776
34775
|
snapTo = "grid"
|
|
34777
34776
|
} = props.graphState;
|
|
34777
|
+
const graphConfig = useGraphConfig();
|
|
34778
|
+
|
|
34779
|
+
// Ref to implement the dragging behavior on a Limited/Closed Polygon.
|
|
34780
|
+
const polygonRef = React__namespace.useRef(null);
|
|
34781
|
+
// Ref to manage dynamic focus for the points in Unlimited Polygon
|
|
34782
|
+
const pointsRef = React__namespace.useRef([]);
|
|
34783
|
+
// Ref to manage the last move time for a Limited Polygon.
|
|
34784
|
+
const lastMoveTimeRef = React__namespace.useRef(0);
|
|
34785
|
+
|
|
34786
|
+
// Dimensions to build the graph overlay for Unlimited Polgyon.
|
|
34778
34787
|
const {
|
|
34779
|
-
|
|
34780
|
-
} =
|
|
34788
|
+
range: [x, y]
|
|
34789
|
+
} = graphConfig;
|
|
34790
|
+
const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
|
|
34781
34791
|
|
|
34782
34792
|
// TODO(benchristel): can the default set of points be removed here? I don't
|
|
34783
34793
|
// think coords can be null.
|
|
34784
34794
|
const points = coords ?? [[0, 0]];
|
|
34785
|
-
|
|
34795
|
+
|
|
34796
|
+
// Logic to build the dragging experience. Primarily used by Limited Polygon.
|
|
34786
34797
|
const dragReferencePoint = points[0];
|
|
34787
34798
|
const constrain = ["angles", "sides"].includes(snapTo) ? p => p : p => snap(snapStep, p);
|
|
34788
34799
|
const {
|
|
34789
34800
|
dragging
|
|
34790
34801
|
} = useDraggable({
|
|
34791
|
-
gestureTarget:
|
|
34802
|
+
gestureTarget: polygonRef,
|
|
34792
34803
|
point: dragReferencePoint,
|
|
34793
34804
|
onMove: newPoint => {
|
|
34794
34805
|
const delta = mafs.vec.sub(newPoint, dragReferencePoint);
|
|
@@ -34796,7 +34807,61 @@ const LimitedPolygonGraph = props => {
|
|
|
34796
34807
|
},
|
|
34797
34808
|
constrainKeyboardMovement: constrain
|
|
34798
34809
|
});
|
|
34799
|
-
|
|
34810
|
+
|
|
34811
|
+
// Hover/focus state for Limited Polgyon effects.
|
|
34812
|
+
const [hovered, setHovered] = React__namespace.useState(false);
|
|
34813
|
+
// This is more so required for the re-rendering that occurs when state
|
|
34814
|
+
// updates; specifically with regard to line weighting and polygon focus.
|
|
34815
|
+
const [focusVisible, setFocusVisible] = React__namespace.useState(false);
|
|
34816
|
+
|
|
34817
|
+
// This useEffect is to handle the focus snapping for Unlimited Polygon.
|
|
34818
|
+
React__namespace.useEffect(() => {
|
|
34819
|
+
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34820
|
+
if (focusedIndex != null) {
|
|
34821
|
+
pointsRef.current[focusedIndex]?.focus();
|
|
34822
|
+
}
|
|
34823
|
+
}, [props.graphState.focusedPointIndex, pointsRef]);
|
|
34824
|
+
const statefulProps = {
|
|
34825
|
+
...props,
|
|
34826
|
+
graphConfig,
|
|
34827
|
+
polygonRef,
|
|
34828
|
+
pointsRef,
|
|
34829
|
+
lastMoveTimeRef,
|
|
34830
|
+
left,
|
|
34831
|
+
top,
|
|
34832
|
+
dragging,
|
|
34833
|
+
points,
|
|
34834
|
+
constrain,
|
|
34835
|
+
hovered,
|
|
34836
|
+
setHovered,
|
|
34837
|
+
focusVisible,
|
|
34838
|
+
setFocusVisible
|
|
34839
|
+
};
|
|
34840
|
+
return numSides === "unlimited" ? UnlimitedPolygonGraph(statefulProps) : LimitedPolygonGraph(statefulProps);
|
|
34841
|
+
};
|
|
34842
|
+
const LimitedPolygonGraph = statefulProps => {
|
|
34843
|
+
const {
|
|
34844
|
+
dispatch,
|
|
34845
|
+
hovered,
|
|
34846
|
+
setHovered,
|
|
34847
|
+
focusVisible,
|
|
34848
|
+
setFocusVisible,
|
|
34849
|
+
graphConfig,
|
|
34850
|
+
polygonRef,
|
|
34851
|
+
lastMoveTimeRef,
|
|
34852
|
+
dragging,
|
|
34853
|
+
points,
|
|
34854
|
+
constrain
|
|
34855
|
+
} = statefulProps;
|
|
34856
|
+
const {
|
|
34857
|
+
showAngles,
|
|
34858
|
+
showSides,
|
|
34859
|
+
range,
|
|
34860
|
+
snapTo = "grid"
|
|
34861
|
+
} = statefulProps.graphState;
|
|
34862
|
+
const {
|
|
34863
|
+
disableKeyboardInteraction
|
|
34864
|
+
} = graphConfig;
|
|
34800
34865
|
const lines = getLines(points);
|
|
34801
34866
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
34802
34867
|
points: [...points],
|
|
@@ -34835,7 +34900,7 @@ const LimitedPolygonGraph = props => {
|
|
|
34835
34900
|
points: [...points],
|
|
34836
34901
|
color: "transparent",
|
|
34837
34902
|
svgPolygonProps: {
|
|
34838
|
-
ref,
|
|
34903
|
+
ref: polygonRef,
|
|
34839
34904
|
tabIndex: disableKeyboardInteraction ? -1 : 0,
|
|
34840
34905
|
strokeWidth: TARGET_SIZE,
|
|
34841
34906
|
style: {
|
|
@@ -34847,13 +34912,13 @@ const LimitedPolygonGraph = props => {
|
|
|
34847
34912
|
// Required to remove line weighting when user clicks away
|
|
34848
34913
|
// from the focused polygon
|
|
34849
34914
|
onKeyDownCapture: () => {
|
|
34850
|
-
setFocusVisible(hasFocusVisible(
|
|
34915
|
+
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
34851
34916
|
},
|
|
34852
34917
|
// Required for lines to darken on focus
|
|
34853
|
-
onFocus: () => setFocusVisible(hasFocusVisible(
|
|
34918
|
+
onFocus: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
34854
34919
|
// Required for line weighting to update on blur. Without this,
|
|
34855
34920
|
// the user has to hover over the shape for it to update
|
|
34856
|
-
onBlur: () => setFocusVisible(hasFocusVisible(
|
|
34921
|
+
onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
34857
34922
|
className: "movable-polygon"
|
|
34858
34923
|
}
|
|
34859
34924
|
}), points.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
@@ -34865,94 +34930,86 @@ const LimitedPolygonGraph = props => {
|
|
|
34865
34930
|
const now = Date.now();
|
|
34866
34931
|
const targetFPS = 40;
|
|
34867
34932
|
const moveThresholdTime = 1000 / targetFPS;
|
|
34868
|
-
if (now -
|
|
34933
|
+
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
34869
34934
|
dispatch(actions.polygon.movePoint(i, destination));
|
|
34870
|
-
|
|
34935
|
+
lastMoveTimeRef.current = now;
|
|
34871
34936
|
}
|
|
34872
34937
|
}
|
|
34873
34938
|
})));
|
|
34874
34939
|
};
|
|
34875
|
-
const UnlimitedPolygonGraph =
|
|
34940
|
+
const UnlimitedPolygonGraph = statefulProps => {
|
|
34876
34941
|
const {
|
|
34877
|
-
dispatch
|
|
34878
|
-
|
|
34942
|
+
dispatch,
|
|
34943
|
+
graphConfig,
|
|
34944
|
+
left,
|
|
34945
|
+
top,
|
|
34946
|
+
pointsRef,
|
|
34947
|
+
points
|
|
34948
|
+
} = statefulProps;
|
|
34879
34949
|
const {
|
|
34880
34950
|
coords,
|
|
34881
34951
|
closedPolygon
|
|
34882
|
-
} =
|
|
34883
|
-
const graphConfig = useGraphConfig();
|
|
34884
|
-
const {
|
|
34885
|
-
range: [x, y],
|
|
34886
|
-
graphDimensionsInPixels
|
|
34887
|
-
} = graphConfig;
|
|
34888
|
-
const widthPx = graphDimensionsInPixels[0];
|
|
34889
|
-
const heightPx = graphDimensionsInPixels[1];
|
|
34890
|
-
const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
|
|
34891
|
-
const pointRef = React__namespace.useRef([]);
|
|
34952
|
+
} = statefulProps.graphState;
|
|
34892
34953
|
|
|
34893
|
-
//
|
|
34894
|
-
// think coords can be null.
|
|
34895
|
-
const points = coords ?? [[0, 0]];
|
|
34896
|
-
React__namespace.useEffect(() => {
|
|
34897
|
-
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34898
|
-
if (focusedIndex != null) {
|
|
34899
|
-
pointRef.current[focusedIndex]?.focus();
|
|
34900
|
-
}
|
|
34901
|
-
}, [props.graphState.focusedPointIndex, pointRef]);
|
|
34954
|
+
// If the polygon is closed, return a LimitedPolygon component.
|
|
34902
34955
|
if (closedPolygon) {
|
|
34903
34956
|
const closedPolygonProps = {
|
|
34904
|
-
...
|
|
34957
|
+
...statefulProps,
|
|
34905
34958
|
numSides: coords.length
|
|
34906
34959
|
};
|
|
34907
34960
|
return /*#__PURE__*/React__namespace.createElement(LimitedPolygonGraph, closedPolygonProps);
|
|
34908
|
-
}
|
|
34909
|
-
|
|
34961
|
+
}
|
|
34962
|
+
const {
|
|
34963
|
+
graphDimensionsInPixels
|
|
34964
|
+
} = graphConfig;
|
|
34965
|
+
const widthPx = graphDimensionsInPixels[0];
|
|
34966
|
+
const heightPx = graphDimensionsInPixels[1];
|
|
34967
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
|
|
34968
|
+
style: {
|
|
34969
|
+
fill: "rgba(0,0,0,0)",
|
|
34970
|
+
cursor: "crosshair"
|
|
34971
|
+
},
|
|
34972
|
+
width: widthPx,
|
|
34973
|
+
height: heightPx,
|
|
34974
|
+
x: left,
|
|
34975
|
+
y: top,
|
|
34976
|
+
onClick: event => {
|
|
34977
|
+
const elementRect = event.currentTarget.getBoundingClientRect();
|
|
34978
|
+
const x = event.clientX - elementRect.x;
|
|
34979
|
+
const y = event.clientY - elementRect.y;
|
|
34980
|
+
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34981
|
+
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
34982
|
+
}
|
|
34983
|
+
}), /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
|
|
34984
|
+
points: [...points],
|
|
34985
|
+
color: "var(--movable-line-stroke-color)",
|
|
34986
|
+
svgPolylineProps: {
|
|
34987
|
+
strokeWidth: "var(--movable-line-stroke-weight)",
|
|
34910
34988
|
style: {
|
|
34911
|
-
fill: "
|
|
34912
|
-
cursor: "crosshair"
|
|
34913
|
-
},
|
|
34914
|
-
width: widthPx,
|
|
34915
|
-
height: heightPx,
|
|
34916
|
-
x: left,
|
|
34917
|
-
y: top,
|
|
34918
|
-
onClick: event => {
|
|
34919
|
-
const elementRect = event.currentTarget.getBoundingClientRect();
|
|
34920
|
-
const x = event.clientX - elementRect.x;
|
|
34921
|
-
const y = event.clientY - elementRect.y;
|
|
34922
|
-
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34923
|
-
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
34924
|
-
}
|
|
34925
|
-
}), /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
|
|
34926
|
-
points: [...points],
|
|
34927
|
-
color: "var(--movable-line-stroke-color)",
|
|
34928
|
-
svgPolylineProps: {
|
|
34929
|
-
strokeWidth: "var(--movable-line-stroke-weight)",
|
|
34930
|
-
style: {
|
|
34931
|
-
fill: "transparent"
|
|
34932
|
-
}
|
|
34989
|
+
fill: "transparent"
|
|
34933
34990
|
}
|
|
34934
|
-
}
|
|
34935
|
-
|
|
34936
|
-
|
|
34937
|
-
|
|
34938
|
-
|
|
34939
|
-
|
|
34940
|
-
|
|
34941
|
-
|
|
34942
|
-
|
|
34943
|
-
|
|
34944
|
-
|
|
34945
|
-
|
|
34946
|
-
|
|
34947
|
-
|
|
34948
|
-
|
|
34949
|
-
|
|
34950
|
-
|
|
34951
|
-
|
|
34952
|
-
dispatch(actions.polygon.clickPoint(i));
|
|
34991
|
+
}
|
|
34992
|
+
}), coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34993
|
+
key: i,
|
|
34994
|
+
point: point,
|
|
34995
|
+
sequenceNumber: i + 1,
|
|
34996
|
+
onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
|
|
34997
|
+
ref: ref => {
|
|
34998
|
+
pointsRef.current[i] = ref;
|
|
34999
|
+
},
|
|
35000
|
+
onFocus: () => {
|
|
35001
|
+
dispatch(actions.polygon.focusPoint(i));
|
|
35002
|
+
},
|
|
35003
|
+
onClick: () => {
|
|
35004
|
+
// If the point being clicked is the first point and
|
|
35005
|
+
// there's enough points to form a polygon (3 or more)
|
|
35006
|
+
// Close the shape before setting focus.
|
|
35007
|
+
if (i === 0 && coords.length >= 3) {
|
|
35008
|
+
dispatch(actions.polygon.closePolygon());
|
|
34953
35009
|
}
|
|
34954
|
-
|
|
34955
|
-
|
|
35010
|
+
dispatch(actions.polygon.clickPoint(i));
|
|
35011
|
+
}
|
|
35012
|
+
})));
|
|
34956
35013
|
};
|
|
34957
35014
|
function getLines(points) {
|
|
34958
35015
|
return points.map((point, i) => {
|
|
@@ -34964,17 +35021,13 @@ const hasFocusVisible = element => {
|
|
|
34964
35021
|
const matches = selector => element?.matches(selector) ?? false;
|
|
34965
35022
|
try {
|
|
34966
35023
|
return matches(":focus-visible");
|
|
34967
|
-
} catch
|
|
35024
|
+
} catch {
|
|
34968
35025
|
// jsdom doesn't support :focus-visible
|
|
34969
35026
|
// (see https://github.com/jsdom/jsdom/issues/3426),
|
|
34970
35027
|
// so the call to matches(":focus-visible") will fail in tests.
|
|
34971
35028
|
return matches(":focus");
|
|
34972
35029
|
}
|
|
34973
35030
|
};
|
|
34974
|
-
const PolygonGraph = props => {
|
|
34975
|
-
const numSides = props.graphState.numSides;
|
|
34976
|
-
return numSides === "unlimited" ? UnlimitedPolygonGraph(props) : LimitedPolygonGraph(props);
|
|
34977
|
-
};
|
|
34978
35031
|
|
|
34979
35032
|
function renderQuadraticGraph(state, dispatch) {
|
|
34980
35033
|
return {
|
|
@@ -40395,7 +40448,7 @@ class Sortable extends React__namespace.Component {
|
|
|
40395
40448
|
this.setState({
|
|
40396
40449
|
items
|
|
40397
40450
|
}, () => {
|
|
40398
|
-
this.props.onMeasure
|
|
40451
|
+
this.props.onMeasure?.({
|
|
40399
40452
|
widths: widths,
|
|
40400
40453
|
heights: heights
|
|
40401
40454
|
});
|
|
@@ -40445,7 +40498,7 @@ class Sortable extends React__namespace.Component {
|
|
|
40445
40498
|
this.setState({
|
|
40446
40499
|
items: nextItems
|
|
40447
40500
|
});
|
|
40448
|
-
this.props.onChange
|
|
40501
|
+
this.props.onChange?.({});
|
|
40449
40502
|
}
|
|
40450
40503
|
onMouseMove(key) {
|
|
40451
40504
|
// Dragging: Rearrange items based on draggable's position
|
|
@@ -40525,7 +40578,7 @@ class Sortable extends React__namespace.Component {
|
|
|
40525
40578
|
// HACK: We need to know *that* the widget changed, but currently it's
|
|
40526
40579
|
// not set up in a nice way to tell us *how* it changed, since the
|
|
40527
40580
|
// permutation of the items is stored in state.
|
|
40528
|
-
this.props.onChange
|
|
40581
|
+
this.props.onChange?.({});
|
|
40529
40582
|
});
|
|
40530
40583
|
|
|
40531
40584
|
// @ts-expect-error - TS2339 - Property 'animationFrameRequest' does not exist on type 'Sortable'.
|
|
@@ -43250,7 +43303,7 @@ class Card extends React__namespace.Component {
|
|
|
43250
43303
|
dragging: true
|
|
43251
43304
|
});
|
|
43252
43305
|
this.bindMouseMoveUp();
|
|
43253
|
-
this.props.onMouseDown
|
|
43306
|
+
this.props.onMouseDown?.(loc, this);
|
|
43254
43307
|
}
|
|
43255
43308
|
};
|
|
43256
43309
|
onMouseMove = event => {
|
|
@@ -43260,7 +43313,7 @@ class Card extends React__namespace.Component {
|
|
|
43260
43313
|
event.preventDefault();
|
|
43261
43314
|
const loc = Util.extractPointerLocation(event);
|
|
43262
43315
|
if (loc) {
|
|
43263
|
-
this.props.onMouseMove
|
|
43316
|
+
this.props.onMouseMove?.(loc);
|
|
43264
43317
|
}
|
|
43265
43318
|
};
|
|
43266
43319
|
onMouseUp = event => {
|
|
@@ -43271,7 +43324,7 @@ class Card extends React__namespace.Component {
|
|
|
43271
43324
|
dragging: false
|
|
43272
43325
|
});
|
|
43273
43326
|
this.unbindMouseMoveUp();
|
|
43274
|
-
this.props.onMouseUp
|
|
43327
|
+
this.props.onMouseUp?.(loc);
|
|
43275
43328
|
}
|
|
43276
43329
|
};
|
|
43277
43330
|
render() {
|
|
@@ -45477,7 +45530,7 @@ const init = function () {
|
|
|
45477
45530
|
|
|
45478
45531
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45479
45532
|
const libName = "@khanacademy/perseus";
|
|
45480
|
-
const libVersion = "
|
|
45533
|
+
const libVersion = "48.0.0";
|
|
45481
45534
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45482
45535
|
|
|
45483
45536
|
const apiVersion = {
|
|
@@ -46237,28 +46290,6 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
46237
46290
|
return this.questionRenderer.getUserInputMap();
|
|
46238
46291
|
}
|
|
46239
46292
|
|
|
46240
|
-
/**
|
|
46241
|
-
* Grades the item.
|
|
46242
|
-
*
|
|
46243
|
-
* @deprecated use scorePerseusItem
|
|
46244
|
-
*/
|
|
46245
|
-
scoreInput() {
|
|
46246
|
-
const guess = this.getUserInput();
|
|
46247
|
-
const score = scorePerseusItem(this.props.item.question, guess, this.context.strings, this.context.locale);
|
|
46248
|
-
|
|
46249
|
-
// Continue to include an empty guess for the now defunct answer area.
|
|
46250
|
-
// TODO(alex): Check whether we rely on the format here for
|
|
46251
|
-
// analyzing ProblemLogs. If not, remove this layer.
|
|
46252
|
-
const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
|
|
46253
|
-
const keScore = keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
|
|
46254
|
-
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
46255
|
-
this.setState({
|
|
46256
|
-
questionCompleted: keScore.correct,
|
|
46257
|
-
questionHighlightedWidgets: emptyQuestionAreaWidgets
|
|
46258
|
-
});
|
|
46259
|
-
return keScore;
|
|
46260
|
-
}
|
|
46261
|
-
|
|
46262
46293
|
/**
|
|
46263
46294
|
* Returns an array of all widget IDs in the order they occur in
|
|
46264
46295
|
* the question content.
|