@khanacademy/perseus 21.0.0 → 21.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.js +519 -117
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +532 -119
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +1 -1
- package/dist/perseus-types.d.ts +6 -2
- package/dist/server-item-renderer.d.ts +1 -1
- package/dist/types.d.ts +3 -1
- package/dist/util/extract-perseus-data.d.ts +2 -2
- package/dist/util.d.ts +3 -0
- package/dist/widgets/expression.d.ts +6 -6
- package/dist/widgets/interactive-graph.d.ts +6 -3
- package/dist/widgets/interactive-graphs/axis-arrows.d.ts +2 -0
- package/dist/widgets/interactive-graphs/axis-ticks.d.ts +8 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-line.d.ts +6 -4
- package/dist/widgets/interactive-graphs/graphs/point.d.ts +5 -0
- package/dist/widgets/interactive-graphs/grid.d.ts +14 -3
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +15 -74
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +1 -1
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +23 -3
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-reducer.d.ts +1 -1
- package/dist/widgets/interactive-graphs/reducer/use-graph-state.d.ts +11 -0
- package/dist/widgets/interactive-graphs/types.d.ts +30 -9
- package/dist/widgets/interactive-graphs/utils.d.ts +4 -2
- package/dist/widgets/label-image.d.ts +2 -2
- package/package.json +1 -1
package/dist/es/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as i18n from '@khanacademy/wonder-blocks-i18n';
|
|
|
7
7
|
import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
|
|
8
8
|
import classNames from 'classnames';
|
|
9
9
|
import * as React from 'react';
|
|
10
|
-
import React__default, { useState, useEffect, useRef } from 'react';
|
|
10
|
+
import React__default, { useState, useEffect, useRef, createContext } from 'react';
|
|
11
11
|
import _, { debounce as debounce$1 } from 'underscore';
|
|
12
12
|
import Clickable from '@khanacademy/wonder-blocks-clickable';
|
|
13
13
|
import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
|
|
@@ -3258,6 +3258,15 @@ const textarea = {
|
|
|
3258
3258
|
*/
|
|
3259
3259
|
const unescapeMathMode$1 = label => label.startsWith("$") && label.endsWith("$") ? label.slice(1, -1) : label;
|
|
3260
3260
|
const random$1 = seededRNG$1(new Date().getTime() & 0xffffffff);
|
|
3261
|
+
|
|
3262
|
+
// TODO(benchristel): in the future, we may want to make deepClone work for
|
|
3263
|
+
// Record<string, Cloneable> as well. Currently, it only does arrays.
|
|
3264
|
+
function deepClone(obj) {
|
|
3265
|
+
if (Array.isArray(obj)) {
|
|
3266
|
+
return obj.map(deepClone);
|
|
3267
|
+
}
|
|
3268
|
+
return obj;
|
|
3269
|
+
}
|
|
3261
3270
|
const Util = {
|
|
3262
3271
|
inputPathsEqual,
|
|
3263
3272
|
nestedMap: nestedMap$2,
|
|
@@ -3303,7 +3312,8 @@ const Util = {
|
|
|
3303
3312
|
getDataUrl: getDataUrl,
|
|
3304
3313
|
textarea,
|
|
3305
3314
|
unescapeMathMode: unescapeMathMode$1,
|
|
3306
|
-
random: random$1
|
|
3315
|
+
random: random$1,
|
|
3316
|
+
deepClone
|
|
3307
3317
|
};
|
|
3308
3318
|
|
|
3309
3319
|
// Derived from the MIT-licensed:
|
|
@@ -24901,9 +24911,22 @@ class Explanation extends React.Component {
|
|
|
24901
24911
|
// Special styling is needed to fit the button in a block of text without throwing off the line spacing.
|
|
24902
24912
|
// While the button is not normally included in a block of text, it needs to be able to accommodate such a case.
|
|
24903
24913
|
const buttonStyleOverrides = {
|
|
24904
|
-
height: "
|
|
24914
|
+
height: "auto",
|
|
24905
24915
|
marginLeft: "-2px",
|
|
24906
|
-
|
|
24916
|
+
// transfer space on the left side to the padding
|
|
24917
|
+
marginRight: "2px",
|
|
24918
|
+
// adds space between the right border and any text that follows
|
|
24919
|
+
paddingLeft: "2px" // adds space between the left border and the button text
|
|
24920
|
+
};
|
|
24921
|
+
|
|
24922
|
+
const labelStyle = {
|
|
24923
|
+
fontSize: "18px",
|
|
24924
|
+
lineHeight: "20px",
|
|
24925
|
+
// The following property adjusts the large space between the button text and the caret icon.
|
|
24926
|
+
// Since we are unable to adjust the styling of the icon (where the extra space exists),
|
|
24927
|
+
// we are adjusting it on the text label by using a negative margin.
|
|
24928
|
+
marginRight: "-6px",
|
|
24929
|
+
"white-space": "normal"
|
|
24907
24930
|
};
|
|
24908
24931
|
const contentStyling = [styles$p.content, this.state.expanded ? styles$p.contentExpanded : styles$p.contentCollapsed, allowTransition && (this.state.expanded ? styles$p.transitionExpanded : styles$p.transitionCollapsed)];
|
|
24909
24932
|
return /*#__PURE__*/React.createElement(UniqueIDProvider, {
|
|
@@ -24914,8 +24937,9 @@ class Explanation extends React.Component {
|
|
|
24914
24937
|
"aria-controls": ids.get("content"),
|
|
24915
24938
|
endIcon: caretIcon,
|
|
24916
24939
|
kind: "tertiary",
|
|
24940
|
+
labelStyle: labelStyle,
|
|
24917
24941
|
onClick: this._onClick,
|
|
24918
|
-
size: "
|
|
24942
|
+
size: "small",
|
|
24919
24943
|
style: buttonStyleOverrides
|
|
24920
24944
|
}, promptText), /*#__PURE__*/React.createElement(View, {
|
|
24921
24945
|
id: ids.get("content"),
|
|
@@ -28190,6 +28214,36 @@ const moveAll = delta => ({
|
|
|
28190
28214
|
type: MOVE_ALL,
|
|
28191
28215
|
delta
|
|
28192
28216
|
});
|
|
28217
|
+
const MOVE_POINT = "move-point";
|
|
28218
|
+
function movePoint(index, destination) {
|
|
28219
|
+
return {
|
|
28220
|
+
type: MOVE_POINT,
|
|
28221
|
+
index,
|
|
28222
|
+
destination
|
|
28223
|
+
};
|
|
28224
|
+
}
|
|
28225
|
+
const CHANGE_SNAP_STEP = "change-snap-step";
|
|
28226
|
+
function changeSnapStep(snapStep) {
|
|
28227
|
+
return {
|
|
28228
|
+
type: CHANGE_SNAP_STEP,
|
|
28229
|
+
snapStep
|
|
28230
|
+
};
|
|
28231
|
+
}
|
|
28232
|
+
const CHANGE_RANGE = "change-range";
|
|
28233
|
+
function changeRange(range) {
|
|
28234
|
+
return {
|
|
28235
|
+
type: CHANGE_RANGE,
|
|
28236
|
+
range
|
|
28237
|
+
};
|
|
28238
|
+
}
|
|
28239
|
+
|
|
28240
|
+
const GraphStateContext = /*#__PURE__*/createContext({
|
|
28241
|
+
state: {},
|
|
28242
|
+
dispatch: () => {}
|
|
28243
|
+
});
|
|
28244
|
+
function useGraphState() {
|
|
28245
|
+
return React__default.useContext(GraphStateContext);
|
|
28246
|
+
}
|
|
28193
28247
|
|
|
28194
28248
|
/**
|
|
28195
28249
|
* 44 is touch best practice and AAA compliant for WCAG
|
|
@@ -28216,6 +28270,11 @@ const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.
|
|
|
28216
28270
|
const extent = ranges[i][1] - ranges[i][0];
|
|
28217
28271
|
return (coord + ranges[i][1]) / extent;
|
|
28218
28272
|
}));
|
|
28273
|
+
function snap(snapStep, point) {
|
|
28274
|
+
const [requestedX, requestedY] = point;
|
|
28275
|
+
const [snapX, snapY] = snapStep;
|
|
28276
|
+
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
28277
|
+
}
|
|
28219
28278
|
|
|
28220
28279
|
const useTransform = (...points) => {
|
|
28221
28280
|
const {
|
|
@@ -28271,6 +28330,9 @@ const getLines = points => points.map((point, i) => {
|
|
|
28271
28330
|
|
|
28272
28331
|
const defaultStroke = "var(--movable-line-stroke-color)";
|
|
28273
28332
|
const MovableLine = props => {
|
|
28333
|
+
const {
|
|
28334
|
+
state
|
|
28335
|
+
} = useGraphState();
|
|
28274
28336
|
const {
|
|
28275
28337
|
start,
|
|
28276
28338
|
end,
|
|
@@ -28295,7 +28357,7 @@ const MovableLine = props => {
|
|
|
28295
28357
|
onMove: newPoint => {
|
|
28296
28358
|
onMove(vec.sub(newPoint, midpoint));
|
|
28297
28359
|
},
|
|
28298
|
-
constrain: p => p
|
|
28360
|
+
constrain: p => snap(state.snapStep, p)
|
|
28299
28361
|
});
|
|
28300
28362
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("g", {
|
|
28301
28363
|
ref: line,
|
|
@@ -28346,7 +28408,13 @@ function SVGLine(props) {
|
|
|
28346
28408
|
});
|
|
28347
28409
|
}
|
|
28348
28410
|
|
|
28411
|
+
// The hitbox size of 48px by 48px is preserved from the legacy interactive
|
|
28412
|
+
// graph.
|
|
28413
|
+
const hitboxSizePx = 48;
|
|
28349
28414
|
const StyledMovablePoint = props => {
|
|
28415
|
+
const {
|
|
28416
|
+
state
|
|
28417
|
+
} = useGraphState();
|
|
28350
28418
|
const hitboxRef = useRef(null);
|
|
28351
28419
|
const {
|
|
28352
28420
|
point,
|
|
@@ -28359,7 +28427,7 @@ const StyledMovablePoint = props => {
|
|
|
28359
28427
|
gestureTarget: hitboxRef,
|
|
28360
28428
|
point,
|
|
28361
28429
|
onMove,
|
|
28362
|
-
constrain: p => p
|
|
28430
|
+
constrain: p => snap(state.snapStep, p)
|
|
28363
28431
|
});
|
|
28364
28432
|
const [[x, y]] = useTransform(point);
|
|
28365
28433
|
return /*#__PURE__*/React.createElement("g", {
|
|
@@ -28374,7 +28442,7 @@ const StyledMovablePoint = props => {
|
|
|
28374
28442
|
}
|
|
28375
28443
|
}, /*#__PURE__*/React.createElement("circle", {
|
|
28376
28444
|
className: "movable-point-hitbox",
|
|
28377
|
-
r:
|
|
28445
|
+
r: hitboxSizePx / 2,
|
|
28378
28446
|
cx: x,
|
|
28379
28447
|
cy: y
|
|
28380
28448
|
}), /*#__PURE__*/React.createElement("circle", {
|
|
@@ -28412,7 +28480,9 @@ const SegmentGraph = props => {
|
|
|
28412
28480
|
onMoveLine: delta => {
|
|
28413
28481
|
dispatch(moveLine(i, delta));
|
|
28414
28482
|
},
|
|
28415
|
-
onMovePoint: (endpointIndex, destination) =>
|
|
28483
|
+
onMovePoint: (endpointIndex, destination) => {
|
|
28484
|
+
dispatch(moveControlPoint(endpointIndex, destination, i));
|
|
28485
|
+
},
|
|
28416
28486
|
"data-testid": "segment" + i
|
|
28417
28487
|
})));
|
|
28418
28488
|
};
|
|
@@ -28701,7 +28771,8 @@ const PolygonGraph = props => {
|
|
|
28701
28771
|
type,
|
|
28702
28772
|
showAngles,
|
|
28703
28773
|
showSides,
|
|
28704
|
-
range
|
|
28774
|
+
range,
|
|
28775
|
+
snapStep
|
|
28705
28776
|
} = props.graphState;
|
|
28706
28777
|
const points = coords != null ? coords : [[0, 0]];
|
|
28707
28778
|
const pointsSum = points.reduce((acc, point) => vec.add(acc, point), [0, 0]);
|
|
@@ -28716,7 +28787,7 @@ const PolygonGraph = props => {
|
|
|
28716
28787
|
const delta = vec.sub(newPoint, midpoint);
|
|
28717
28788
|
dispatch(moveAll(delta));
|
|
28718
28789
|
},
|
|
28719
|
-
constrain: p => p
|
|
28790
|
+
constrain: p => snap(snapStep, p)
|
|
28720
28791
|
});
|
|
28721
28792
|
const active = hovered || focused || dragging;
|
|
28722
28793
|
const lines = getLines(points);
|
|
@@ -28768,22 +28839,179 @@ const PolygonGraph = props => {
|
|
|
28768
28839
|
}), points.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
28769
28840
|
key: "point-" + i,
|
|
28770
28841
|
point: point,
|
|
28771
|
-
onMove: destination => dispatch(
|
|
28842
|
+
onMove: destination => dispatch(movePoint(i, destination)),
|
|
28772
28843
|
"data-testid": type + i
|
|
28773
28844
|
})));
|
|
28774
28845
|
};
|
|
28775
28846
|
|
|
28776
|
-
|
|
28777
|
-
const
|
|
28778
|
-
|
|
28779
|
-
|
|
28780
|
-
|
|
28781
|
-
|
|
28782
|
-
|
|
28783
|
-
|
|
28784
|
-
|
|
28785
|
-
|
|
28786
|
-
|
|
28847
|
+
function PointGraph(props) {
|
|
28848
|
+
const {
|
|
28849
|
+
dispatch
|
|
28850
|
+
} = props;
|
|
28851
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
28852
|
+
key: i,
|
|
28853
|
+
point: point,
|
|
28854
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
28855
|
+
})));
|
|
28856
|
+
}
|
|
28857
|
+
|
|
28858
|
+
function Arrow(props) {
|
|
28859
|
+
const {
|
|
28860
|
+
userTransform,
|
|
28861
|
+
viewTransform
|
|
28862
|
+
} = useTransformContext();
|
|
28863
|
+
const point = [props.x, props.y];
|
|
28864
|
+
const userTransformedPoint = vec.transform(point, userTransform);
|
|
28865
|
+
const viewTransformedPoint = vec.transform(userTransformedPoint, viewTransform);
|
|
28866
|
+
return /*#__PURE__*/React__default.createElement("g", {
|
|
28867
|
+
transform: `translate(${viewTransformedPoint[0]} ${viewTransformedPoint[1]}) rotate(${props.rotate}) scale(1.25)`
|
|
28868
|
+
}, /*#__PURE__*/React__default.createElement("g", {
|
|
28869
|
+
transform: "translate(-1)"
|
|
28870
|
+
}, /*#__PURE__*/React__default.createElement("path", {
|
|
28871
|
+
d: "M-3 4 C-2.75 2.5 0 0.25 0.75 0C0 -0.25 -2.75 -2.5 -3 -4",
|
|
28872
|
+
fill: "none"
|
|
28873
|
+
})));
|
|
28874
|
+
}
|
|
28875
|
+
function AxisArrows() {
|
|
28876
|
+
const {
|
|
28877
|
+
state
|
|
28878
|
+
} = useGraphState();
|
|
28879
|
+
const range = state.range;
|
|
28880
|
+
const [xMin, xMax] = range[0];
|
|
28881
|
+
const [yMin, yMax] = range[1];
|
|
28882
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Arrow, {
|
|
28883
|
+
x: xMax,
|
|
28884
|
+
y: 0,
|
|
28885
|
+
rotate: 0
|
|
28886
|
+
}), /*#__PURE__*/React__default.createElement(Arrow, {
|
|
28887
|
+
x: 0,
|
|
28888
|
+
y: yMin,
|
|
28889
|
+
rotate: 90
|
|
28890
|
+
}), /*#__PURE__*/React__default.createElement(Arrow, {
|
|
28891
|
+
x: xMin,
|
|
28892
|
+
y: 0,
|
|
28893
|
+
rotate: 180
|
|
28894
|
+
}), /*#__PURE__*/React__default.createElement(Arrow, {
|
|
28895
|
+
x: 0,
|
|
28896
|
+
y: yMax,
|
|
28897
|
+
rotate: 270
|
|
28898
|
+
}));
|
|
28899
|
+
}
|
|
28900
|
+
|
|
28901
|
+
const tickSize = 10;
|
|
28902
|
+
const tickStyle = {
|
|
28903
|
+
stroke: "black",
|
|
28904
|
+
strokeWidth: 1
|
|
28905
|
+
};
|
|
28906
|
+
const YGridTick = ({
|
|
28907
|
+
y
|
|
28908
|
+
}) => {
|
|
28909
|
+
const pointOnAxis = [0, y];
|
|
28910
|
+
const [[xPosition, yPosition]] = useTransform(pointOnAxis);
|
|
28911
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28912
|
+
className: "y-axis-ticks"
|
|
28913
|
+
}, /*#__PURE__*/React.createElement("line", {
|
|
28914
|
+
x1: xPosition - tickSize / 2,
|
|
28915
|
+
y1: yPosition,
|
|
28916
|
+
x2: xPosition + tickSize / 2,
|
|
28917
|
+
y2: yPosition,
|
|
28918
|
+
style: tickStyle
|
|
28919
|
+
}));
|
|
28920
|
+
};
|
|
28921
|
+
const XGridTick = ({
|
|
28922
|
+
x
|
|
28923
|
+
}) => {
|
|
28924
|
+
const pointOnAxis = [x, 0];
|
|
28925
|
+
const [[xPosition, yPosition]] = useTransform(pointOnAxis);
|
|
28926
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28927
|
+
className: "x-axis-ticks"
|
|
28928
|
+
}, /*#__PURE__*/React.createElement("line", {
|
|
28929
|
+
x1: xPosition,
|
|
28930
|
+
y1: yPosition + tickSize / 2,
|
|
28931
|
+
x2: xPosition,
|
|
28932
|
+
y2: yPosition - tickSize / 2,
|
|
28933
|
+
style: tickStyle
|
|
28934
|
+
}));
|
|
28935
|
+
};
|
|
28936
|
+
function generateTickLocations(tickStep, min, max) {
|
|
28937
|
+
const ticks = [];
|
|
28938
|
+
for (let i = 0 + tickStep; i < max; i += tickStep) {
|
|
28939
|
+
ticks.push(i);
|
|
28940
|
+
}
|
|
28941
|
+
for (let i = 0 - tickStep; i > min; i -= tickStep) {
|
|
28942
|
+
ticks.push(i);
|
|
28943
|
+
}
|
|
28944
|
+
return ticks;
|
|
28945
|
+
}
|
|
28946
|
+
const AxisTicks = props => {
|
|
28947
|
+
const range = props.range;
|
|
28948
|
+
const [xMin, xMax] = range[0];
|
|
28949
|
+
const [yMin, yMax] = range[1];
|
|
28950
|
+
const yTickStep = props.tickStep[1];
|
|
28951
|
+
const xTickStep = props.tickStep[0];
|
|
28952
|
+
const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
|
|
28953
|
+
const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
|
|
28954
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28955
|
+
className: "axis-ticks"
|
|
28956
|
+
}, yGridTicks.map(y => {
|
|
28957
|
+
return /*#__PURE__*/React.createElement(YGridTick, {
|
|
28958
|
+
y: y,
|
|
28959
|
+
key: `y-grid-tick-${y}`
|
|
28960
|
+
});
|
|
28961
|
+
}), xGridTicks.map(x => {
|
|
28962
|
+
return /*#__PURE__*/React.createElement(XGridTick, {
|
|
28963
|
+
x: x,
|
|
28964
|
+
key: `x-grid-tick-${x}`
|
|
28965
|
+
});
|
|
28966
|
+
}));
|
|
28967
|
+
};
|
|
28968
|
+
|
|
28969
|
+
/**
|
|
28970
|
+
* lineLabelText get the text that should
|
|
28971
|
+
* be displayed for a specific tick place
|
|
28972
|
+
*
|
|
28973
|
+
* @param {number} n the tick number
|
|
28974
|
+
* @param {number} tickStep how frequently a label should appear
|
|
28975
|
+
* @param {vec.Vector2} range the min/max range on the axis
|
|
28976
|
+
*/
|
|
28977
|
+
const lineLabelText = (n, tickStep, range) => {
|
|
28978
|
+
const [min, max] = range;
|
|
28979
|
+
const isOnStep = n % tickStep === 0;
|
|
28980
|
+
const isNegativeOne = n === -1;
|
|
28981
|
+
const isMin = n === min;
|
|
28982
|
+
const isMax = n === max;
|
|
28983
|
+
const shouldRender = isOnStep && !isNegativeOne && !isMin && !isMax;
|
|
28984
|
+
return shouldRender ? `${n}` : "";
|
|
28985
|
+
};
|
|
28986
|
+
|
|
28987
|
+
/**
|
|
28988
|
+
* axisOptions determine axis options for Mafs
|
|
28989
|
+
*
|
|
28990
|
+
* axisIndex is for grabbing data in an array that contains
|
|
28991
|
+
* data for multiple axes. For example range: [[-10, 10], [-10, 10]]
|
|
28992
|
+
* range[0] is data for the x axis and range[1] is data for the y axis
|
|
28993
|
+
*
|
|
28994
|
+
* @param {GridProps} props
|
|
28995
|
+
* @param {number} axisIndex which axis we're getting options for
|
|
28996
|
+
*/
|
|
28997
|
+
const axisOptions = (props, axisIndex) => {
|
|
28998
|
+
const axisStep = props.tickStep[axisIndex];
|
|
28999
|
+
const axisRange = props.range[axisIndex];
|
|
29000
|
+
return {
|
|
29001
|
+
axis: props.markings === "graph",
|
|
29002
|
+
lines: props.gridStep[axisIndex],
|
|
29003
|
+
labels: n => lineLabelText(n, axisStep, axisRange)
|
|
29004
|
+
};
|
|
29005
|
+
};
|
|
29006
|
+
const Grid = props => {
|
|
29007
|
+
return props.markings === "none" ? null : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Coordinates.Cartesian, {
|
|
29008
|
+
xAxis: axisOptions(props, 0),
|
|
29009
|
+
yAxis: axisOptions(props, 1)
|
|
29010
|
+
}), /*#__PURE__*/React.createElement(AxisTicks, {
|
|
29011
|
+
range: props.range,
|
|
29012
|
+
tickStep: props.tickStep
|
|
29013
|
+
}), props.markings === "graph" && /*#__PURE__*/React.createElement(AxisArrows, null));
|
|
29014
|
+
};
|
|
28787
29015
|
|
|
28788
29016
|
// Displays a stylized open eye: lint warnings are visible
|
|
28789
29017
|
const VisibleIcon = () => /*#__PURE__*/React.createElement("svg", {
|
|
@@ -29480,53 +29708,50 @@ const LegacyGrid = ({
|
|
|
29480
29708
|
return null;
|
|
29481
29709
|
};
|
|
29482
29710
|
|
|
29483
|
-
/** Determine if coords is type CollinearTuple[] */
|
|
29484
|
-
const isCollinearTuples = coords => Array.isArray(coords[0][0]);
|
|
29485
|
-
|
|
29486
|
-
// Generic type makes returned state match input state
|
|
29487
29711
|
function interactiveGraphReducer(state, action) {
|
|
29712
|
+
switch (action.type) {
|
|
29713
|
+
case MOVE_CONTROL_POINT:
|
|
29714
|
+
return doMoveControlPoint(state, action);
|
|
29715
|
+
case MOVE_LINE:
|
|
29716
|
+
return doMoveLine(state, action);
|
|
29717
|
+
case MOVE_ALL:
|
|
29718
|
+
return doMoveAll(state, action);
|
|
29719
|
+
case MOVE_POINT:
|
|
29720
|
+
return doMovePoint(state, action);
|
|
29721
|
+
case CHANGE_SNAP_STEP:
|
|
29722
|
+
return doChangeSnapStep(state, action);
|
|
29723
|
+
case CHANGE_RANGE:
|
|
29724
|
+
return doChangeRange(state, action);
|
|
29725
|
+
default:
|
|
29726
|
+
throw new UnreachableCaseError(action);
|
|
29727
|
+
}
|
|
29728
|
+
}
|
|
29729
|
+
function doMoveControlPoint(state, action) {
|
|
29488
29730
|
const {
|
|
29489
29731
|
snapStep,
|
|
29490
29732
|
range
|
|
29491
29733
|
} = state;
|
|
29492
|
-
|
|
29493
|
-
|
|
29494
|
-
|
|
29495
|
-
|
|
29496
|
-
case
|
|
29734
|
+
switch (state.type) {
|
|
29735
|
+
case "segment":
|
|
29736
|
+
case "linear":
|
|
29737
|
+
case "linear-system":
|
|
29738
|
+
case "ray":
|
|
29497
29739
|
{
|
|
29498
|
-
const newCoords =
|
|
29740
|
+
const newCoords = updateAtIndex({
|
|
29499
29741
|
array: state.coords,
|
|
29500
29742
|
index: action.itemIndex,
|
|
29501
29743
|
update: tuple => setAtIndex({
|
|
29502
29744
|
array: tuple,
|
|
29503
29745
|
index: action.pointIndex,
|
|
29504
|
-
newValue: snap({
|
|
29505
|
-
snapStep,
|
|
29506
|
-
point: bound$1({
|
|
29507
|
-
snapStep,
|
|
29508
|
-
range,
|
|
29509
|
-
point: action.destination
|
|
29510
|
-
})
|
|
29511
|
-
})
|
|
29512
|
-
})
|
|
29513
|
-
}) : setAtIndex({
|
|
29514
|
-
array: state.coords,
|
|
29515
|
-
index: action.pointIndex,
|
|
29516
|
-
newValue: snap({
|
|
29517
|
-
snapStep,
|
|
29518
|
-
point: bound$1({
|
|
29746
|
+
newValue: snap(snapStep, bound$1({
|
|
29519
29747
|
snapStep,
|
|
29520
29748
|
range,
|
|
29521
29749
|
point: action.destination
|
|
29522
|
-
})
|
|
29750
|
+
}))
|
|
29523
29751
|
})
|
|
29524
29752
|
});
|
|
29525
|
-
|
|
29526
|
-
|
|
29527
|
-
// so this may seem redundant, but it's necessary for type safety.
|
|
29528
|
-
const coordsToCheck = isCollinearTuples(newCoords) && action.itemIndex !== undefined ? newCoords[action.itemIndex] : newCoords;
|
|
29529
|
-
if (!isCollinearTuples(coordsToCheck) && coordsOverlap(coordsToCheck)) {
|
|
29753
|
+
const coordsToCheck = newCoords[action.itemIndex];
|
|
29754
|
+
if (coordsOverlap(coordsToCheck)) {
|
|
29530
29755
|
return state;
|
|
29531
29756
|
}
|
|
29532
29757
|
return _extends({}, state, {
|
|
@@ -29534,16 +29759,30 @@ function interactiveGraphReducer(state, action) {
|
|
|
29534
29759
|
coords: newCoords
|
|
29535
29760
|
});
|
|
29536
29761
|
}
|
|
29537
|
-
case
|
|
29762
|
+
case "circle":
|
|
29763
|
+
throw new Error("FIXME implement circle reducer");
|
|
29764
|
+
case "point":
|
|
29765
|
+
case "polygon":
|
|
29766
|
+
throw new Error(`Don't use moveControlPoint for ${state.type} graphs. Use movePoint instead!`);
|
|
29767
|
+
default:
|
|
29768
|
+
throw new UnreachableCaseError(state);
|
|
29769
|
+
}
|
|
29770
|
+
}
|
|
29771
|
+
function doMoveLine(state, action) {
|
|
29772
|
+
const {
|
|
29773
|
+
snapStep,
|
|
29774
|
+
range
|
|
29775
|
+
} = state;
|
|
29776
|
+
switch (state.type) {
|
|
29777
|
+
case "segment":
|
|
29778
|
+
case "linear":
|
|
29779
|
+
case "linear-system":
|
|
29780
|
+
case "ray":
|
|
29538
29781
|
{
|
|
29539
|
-
var _state$coords;
|
|
29540
|
-
if (!isCollinearTuples(state.coords)) {
|
|
29541
|
-
throw new Error("Cannot call this action unless coords is array of CollinearTuple.");
|
|
29542
|
-
}
|
|
29543
29782
|
if (action.itemIndex === undefined) {
|
|
29544
29783
|
throw new Error("Please provide index of line to move");
|
|
29545
29784
|
}
|
|
29546
|
-
const currentLine =
|
|
29785
|
+
const currentLine = state.coords[action.itemIndex];
|
|
29547
29786
|
if (!currentLine) {
|
|
29548
29787
|
throw new Error("No line to move");
|
|
29549
29788
|
}
|
|
@@ -29551,45 +29790,89 @@ function interactiveGraphReducer(state, action) {
|
|
|
29551
29790
|
snapStep,
|
|
29552
29791
|
range
|
|
29553
29792
|
});
|
|
29554
|
-
const newLine = [snap(
|
|
29555
|
-
snapStep,
|
|
29556
|
-
point: vec.add(currentLine[0], change)
|
|
29557
|
-
}), snap({
|
|
29558
|
-
snapStep,
|
|
29559
|
-
point: vec.add(currentLine[1], change)
|
|
29560
|
-
})];
|
|
29793
|
+
const newLine = [snap(snapStep, vec.add(currentLine[0], change)), snap(snapStep, vec.add(currentLine[1], change))];
|
|
29561
29794
|
const newCoords = setAtIndex({
|
|
29562
29795
|
array: state.coords,
|
|
29563
29796
|
index: action.itemIndex,
|
|
29564
29797
|
newValue: newLine
|
|
29565
29798
|
});
|
|
29566
29799
|
return _extends({}, state, {
|
|
29800
|
+
type: state.type,
|
|
29567
29801
|
hasBeenInteractedWith: true,
|
|
29568
29802
|
coords: newCoords
|
|
29569
29803
|
});
|
|
29570
29804
|
}
|
|
29571
|
-
|
|
29805
|
+
default:
|
|
29806
|
+
// The MoveLine action doesn't make sense for other graph types;
|
|
29807
|
+
// ignore it if it somehow happens
|
|
29808
|
+
return state;
|
|
29809
|
+
}
|
|
29810
|
+
}
|
|
29811
|
+
function doMoveAll(state, action) {
|
|
29812
|
+
const {
|
|
29813
|
+
snapStep,
|
|
29814
|
+
range
|
|
29815
|
+
} = state;
|
|
29816
|
+
switch (state.type) {
|
|
29817
|
+
case "polygon":
|
|
29572
29818
|
{
|
|
29573
|
-
if (isCollinearTuples(state.coords)) {
|
|
29574
|
-
throw new Error("Cannot call this action unless coords is array of vectors.");
|
|
29575
|
-
}
|
|
29576
29819
|
const change = getChange(state.coords, action.delta, {
|
|
29577
29820
|
snapStep,
|
|
29578
29821
|
range
|
|
29579
29822
|
});
|
|
29580
|
-
const newCoords = state.coords.map(point => snap(
|
|
29581
|
-
snapStep,
|
|
29582
|
-
point: vec.add(point, change)
|
|
29583
|
-
}));
|
|
29823
|
+
const newCoords = state.coords.map(point => snap(snapStep, vec.add(point, change)));
|
|
29584
29824
|
return _extends({}, state, {
|
|
29585
29825
|
hasBeenInteractedWith: true,
|
|
29586
29826
|
coords: newCoords
|
|
29587
29827
|
});
|
|
29588
29828
|
}
|
|
29589
29829
|
default:
|
|
29590
|
-
|
|
29830
|
+
// MoveAll is not supported for other state types; just ignore it.
|
|
29831
|
+
return state;
|
|
29832
|
+
}
|
|
29833
|
+
}
|
|
29834
|
+
function doMovePoint(state, action) {
|
|
29835
|
+
switch (state.type) {
|
|
29836
|
+
case "point":
|
|
29837
|
+
case "polygon":
|
|
29838
|
+
{
|
|
29839
|
+
return _extends({}, state, {
|
|
29840
|
+
hasBeenInteractedWith: true,
|
|
29841
|
+
coords: setAtIndex({
|
|
29842
|
+
array: state.coords,
|
|
29843
|
+
index: action.index,
|
|
29844
|
+
newValue: snap(state.snapStep, bound$1({
|
|
29845
|
+
snapStep: state.snapStep,
|
|
29846
|
+
range: state.range,
|
|
29847
|
+
point: action.destination
|
|
29848
|
+
}))
|
|
29849
|
+
})
|
|
29850
|
+
});
|
|
29851
|
+
}
|
|
29852
|
+
default:
|
|
29853
|
+
throw new Error("The movePoint action is only for point and polygon graphs");
|
|
29591
29854
|
}
|
|
29592
29855
|
}
|
|
29856
|
+
function doChangeSnapStep(state, action) {
|
|
29857
|
+
if (
|
|
29858
|
+
// Deep equality check since these are arrays
|
|
29859
|
+
_.isEqual(state.snapStep, action.snapStep)) {
|
|
29860
|
+
return state;
|
|
29861
|
+
}
|
|
29862
|
+
return _extends({}, state, {
|
|
29863
|
+
snapStep: action.snapStep
|
|
29864
|
+
});
|
|
29865
|
+
}
|
|
29866
|
+
function doChangeRange(state, action) {
|
|
29867
|
+
if (
|
|
29868
|
+
// Deep equality check since these are arrays
|
|
29869
|
+
_.isEqual(state.range, action.range)) {
|
|
29870
|
+
return state;
|
|
29871
|
+
}
|
|
29872
|
+
return _extends({}, state, {
|
|
29873
|
+
range: action.range
|
|
29874
|
+
});
|
|
29875
|
+
}
|
|
29593
29876
|
const getChange = (coords, delta, constraintOpts) => {
|
|
29594
29877
|
const [deltaX, deltaY] = delta;
|
|
29595
29878
|
const maxMoves = coords.map(point => maxMove(_extends({}, constraintOpts, {
|
|
@@ -29606,15 +29889,6 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
29606
29889
|
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
29607
29890
|
return [dx, dy];
|
|
29608
29891
|
};
|
|
29609
|
-
function snap({
|
|
29610
|
-
snapStep,
|
|
29611
|
-
point
|
|
29612
|
-
}) {
|
|
29613
|
-
const [requestedX, requestedY] = point;
|
|
29614
|
-
const [snapX, snapY] = snapStep;
|
|
29615
|
-
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
29616
|
-
}
|
|
29617
|
-
|
|
29618
29892
|
// Returns the closest point to the given `point` that is within the graph
|
|
29619
29893
|
// bounds given in `state`.
|
|
29620
29894
|
function bound$1({
|
|
@@ -29668,7 +29942,7 @@ function clamp(value, min, max) {
|
|
|
29668
29942
|
const coordsOverlap = coords => coords.some((coord, i) => coords.some((c, j) => i !== j && vector$1.equal(coord, c)));
|
|
29669
29943
|
function updateAtIndex(args) {
|
|
29670
29944
|
const {
|
|
29671
|
-
array
|
|
29945
|
+
array,
|
|
29672
29946
|
index,
|
|
29673
29947
|
update
|
|
29674
29948
|
} = args;
|
|
@@ -29681,13 +29955,12 @@ function updateAtIndex(args) {
|
|
|
29681
29955
|
}
|
|
29682
29956
|
function setAtIndex(args) {
|
|
29683
29957
|
const {
|
|
29684
|
-
array
|
|
29958
|
+
array,
|
|
29685
29959
|
index,
|
|
29686
29960
|
newValue
|
|
29687
29961
|
} = args;
|
|
29688
29962
|
const copy = [...array];
|
|
29689
29963
|
copy[index] = newValue;
|
|
29690
|
-
// restoring readonly to array
|
|
29691
29964
|
return copy;
|
|
29692
29965
|
}
|
|
29693
29966
|
|
|
@@ -29728,19 +30001,32 @@ function initializeGraphState(params) {
|
|
|
29728
30001
|
})
|
|
29729
30002
|
};
|
|
29730
30003
|
case "polygon":
|
|
29731
|
-
return
|
|
30004
|
+
return {
|
|
30005
|
+
type: "polygon",
|
|
29732
30006
|
hasBeenInteractedWith: false,
|
|
29733
30007
|
range,
|
|
29734
|
-
snapStep
|
|
29735
|
-
|
|
30008
|
+
snapStep,
|
|
30009
|
+
showAngles: Boolean(graph.showAngles),
|
|
30010
|
+
showSides: Boolean(graph.showSides),
|
|
29736
30011
|
coords: getPolygonCoords({
|
|
29737
30012
|
graph,
|
|
29738
30013
|
range,
|
|
29739
30014
|
step
|
|
29740
30015
|
})
|
|
29741
|
-
}
|
|
29742
|
-
case "angle":
|
|
30016
|
+
};
|
|
29743
30017
|
case "point":
|
|
30018
|
+
return {
|
|
30019
|
+
type: graph.type,
|
|
30020
|
+
hasBeenInteractedWith: false,
|
|
30021
|
+
range,
|
|
30022
|
+
snapStep,
|
|
30023
|
+
coords: getDefaultPoints({
|
|
30024
|
+
graph,
|
|
30025
|
+
step,
|
|
30026
|
+
range
|
|
30027
|
+
})
|
|
30028
|
+
};
|
|
30029
|
+
case "angle":
|
|
29744
30030
|
case "circle":
|
|
29745
30031
|
case "sinusoid":
|
|
29746
30032
|
case "quadratic":
|
|
@@ -29749,6 +30035,46 @@ function initializeGraphState(params) {
|
|
|
29749
30035
|
throw new UnreachableCaseError(graph);
|
|
29750
30036
|
}
|
|
29751
30037
|
}
|
|
30038
|
+
const getDefaultPoints = ({
|
|
30039
|
+
graph,
|
|
30040
|
+
range,
|
|
30041
|
+
step
|
|
30042
|
+
}) => {
|
|
30043
|
+
var _graph$coords;
|
|
30044
|
+
const numPoints = graph.numPoints || 1;
|
|
30045
|
+
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
30046
|
+
if (coords) {
|
|
30047
|
+
return coords;
|
|
30048
|
+
}
|
|
30049
|
+
switch (numPoints) {
|
|
30050
|
+
case 1:
|
|
30051
|
+
// Back in the day, one point's coords were in graph.coord
|
|
30052
|
+
coords = [graph.coord || [0, 0]];
|
|
30053
|
+
break;
|
|
30054
|
+
case 2:
|
|
30055
|
+
coords = [[-5, 0], [5, 0]];
|
|
30056
|
+
break;
|
|
30057
|
+
case 3:
|
|
30058
|
+
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
30059
|
+
break;
|
|
30060
|
+
case 4:
|
|
30061
|
+
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
30062
|
+
break;
|
|
30063
|
+
case 5:
|
|
30064
|
+
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
30065
|
+
break;
|
|
30066
|
+
case 6:
|
|
30067
|
+
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
30068
|
+
break;
|
|
30069
|
+
default:
|
|
30070
|
+
coords = [];
|
|
30071
|
+
break;
|
|
30072
|
+
}
|
|
30073
|
+
// Transform coords from their -10 to 10 space to 0 to 1
|
|
30074
|
+
// because of the old graph.coord, and also it's easier.
|
|
30075
|
+
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30076
|
+
return normalizePoints(range, step, newCoords);
|
|
30077
|
+
};
|
|
29752
30078
|
|
|
29753
30079
|
// TS v4 doesn't narrow return types, while v5 does.
|
|
29754
30080
|
// Instead of updating to v5, using generic type to relate input and output types.
|
|
@@ -29769,15 +30095,13 @@ function getGradableGraph(state, initialGraph) {
|
|
|
29769
30095
|
});
|
|
29770
30096
|
}
|
|
29771
30097
|
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
29772
|
-
var _state$coords;
|
|
29773
30098
|
return _extends({}, initialGraph, {
|
|
29774
|
-
coords:
|
|
30099
|
+
coords: state.coords[0]
|
|
29775
30100
|
});
|
|
29776
30101
|
}
|
|
29777
30102
|
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
29778
|
-
var _state$coords2;
|
|
29779
30103
|
return _extends({}, initialGraph, {
|
|
29780
|
-
coords:
|
|
30104
|
+
coords: state.coords[0]
|
|
29781
30105
|
});
|
|
29782
30106
|
}
|
|
29783
30107
|
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
@@ -29785,6 +30109,11 @@ function getGradableGraph(state, initialGraph) {
|
|
|
29785
30109
|
coords: state.coords
|
|
29786
30110
|
});
|
|
29787
30111
|
}
|
|
30112
|
+
if (state.type === "point" && initialGraph.type === "point") {
|
|
30113
|
+
return _extends({}, initialGraph, {
|
|
30114
|
+
coords: state.coords
|
|
30115
|
+
});
|
|
30116
|
+
}
|
|
29788
30117
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29789
30118
|
}
|
|
29790
30119
|
const getDefaultSegments = ({
|
|
@@ -29822,10 +30151,10 @@ const getLineCoords = ({
|
|
|
29822
30151
|
range,
|
|
29823
30152
|
step
|
|
29824
30153
|
}) => {
|
|
29825
|
-
var _graph$
|
|
30154
|
+
var _graph$coords2;
|
|
29826
30155
|
return (
|
|
29827
30156
|
// Return two lines for a linear system, one for a ray or linear
|
|
29828
|
-
((_graph$
|
|
30157
|
+
((_graph$coords2 = graph.coords) != null ? _graph$coords2 : graph.type === "linear-system") ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
29829
30158
|
);
|
|
29830
30159
|
};
|
|
29831
30160
|
const getPolygonCoords = ({
|
|
@@ -29833,7 +30162,8 @@ const getPolygonCoords = ({
|
|
|
29833
30162
|
range,
|
|
29834
30163
|
step
|
|
29835
30164
|
}) => {
|
|
29836
|
-
|
|
30165
|
+
var _graph$coords3;
|
|
30166
|
+
let coords = (_graph$coords3 = graph.coords) == null ? void 0 : _graph$coords3.slice();
|
|
29837
30167
|
if (coords) {
|
|
29838
30168
|
return coords;
|
|
29839
30169
|
}
|
|
@@ -29887,6 +30217,13 @@ const renderGraph = props => {
|
|
|
29887
30217
|
graphState: state,
|
|
29888
30218
|
dispatch: dispatch
|
|
29889
30219
|
});
|
|
30220
|
+
case "point":
|
|
30221
|
+
return /*#__PURE__*/React.createElement(PointGraph, {
|
|
30222
|
+
graphState: state,
|
|
30223
|
+
dispatch: dispatch
|
|
30224
|
+
});
|
|
30225
|
+
case "circle":
|
|
30226
|
+
throw new Error("the circle graph type is not yet implemented");
|
|
29890
30227
|
default:
|
|
29891
30228
|
return new UnreachableCaseError(type);
|
|
29892
30229
|
}
|
|
@@ -29894,10 +30231,36 @@ const renderGraph = props => {
|
|
|
29894
30231
|
const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
29895
30232
|
const [width, height] = props.box;
|
|
29896
30233
|
const [state, dispatch] = React.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
30234
|
+
const prevState = useRef(state);
|
|
30235
|
+
useEffect(() => {
|
|
30236
|
+
if (prevState.current !== state) {
|
|
30237
|
+
props.onChange({
|
|
30238
|
+
graph: state
|
|
30239
|
+
});
|
|
30240
|
+
}
|
|
30241
|
+
prevState.current = state;
|
|
30242
|
+
}, [props, state]);
|
|
30243
|
+
|
|
30244
|
+
// Destructuring first to keep useEffect from making excess calls
|
|
30245
|
+
const [xSnap, ySnap] = props.snapStep;
|
|
30246
|
+
useEffect(() => {
|
|
30247
|
+
dispatch(changeSnapStep([xSnap, ySnap]));
|
|
30248
|
+
}, [xSnap, ySnap]);
|
|
30249
|
+
|
|
30250
|
+
// Destructuring first to keep useEffect from making excess calls
|
|
30251
|
+
const [[xMinRange, xMaxRange], [yMinRange, yMaxRange]] = props.range;
|
|
30252
|
+
useEffect(() => {
|
|
30253
|
+
dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
|
|
30254
|
+
}, [xMinRange, xMaxRange, yMinRange, yMaxRange]);
|
|
29897
30255
|
React.useImperativeHandle(ref, () => ({
|
|
29898
30256
|
getUserInput: () => getGradableGraph(state, props.graph)
|
|
29899
30257
|
}));
|
|
29900
|
-
return /*#__PURE__*/React.createElement(
|
|
30258
|
+
return /*#__PURE__*/React.createElement(GraphStateContext.Provider, {
|
|
30259
|
+
value: {
|
|
30260
|
+
state,
|
|
30261
|
+
dispatch
|
|
30262
|
+
}
|
|
30263
|
+
}, /*#__PURE__*/React.createElement(View, {
|
|
29901
30264
|
style: {
|
|
29902
30265
|
width,
|
|
29903
30266
|
height,
|
|
@@ -29923,12 +30286,18 @@ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
29923
30286
|
zoom: false,
|
|
29924
30287
|
width: width,
|
|
29925
30288
|
height: height
|
|
29926
|
-
}, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid,
|
|
30289
|
+
}, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid, {
|
|
30290
|
+
tickStep: props.step,
|
|
30291
|
+
gridStep: props.gridStep,
|
|
30292
|
+
range: props.range,
|
|
30293
|
+
containerSizeClass: props.containerSizeClass,
|
|
30294
|
+
markings: props.markings
|
|
30295
|
+
}), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
|
|
29927
30296
|
lockedFigures: props.lockedFigures
|
|
29928
30297
|
}), renderGraph({
|
|
29929
30298
|
state,
|
|
29930
30299
|
dispatch
|
|
29931
|
-
}))));
|
|
30300
|
+
})))));
|
|
29932
30301
|
});
|
|
29933
30302
|
|
|
29934
30303
|
const {
|
|
@@ -31065,6 +31434,7 @@ class LegacyInteractiveGraph extends React.Component {
|
|
|
31065
31434
|
$(this.angle).on("move", () => {
|
|
31066
31435
|
var _this$angle;
|
|
31067
31436
|
this.onChange({
|
|
31437
|
+
// @ts-expect-error Type '{ coords: any; type: "angle"; showAngles?: boolean | undefined; allowReflexAngles?: boolean | undefined; angleOffsetDeg?: number | undefined; snapDegrees?: number | undefined; match?: "congruent" | undefined; }' is not assignable to type 'InteractiveGraphState | undefined'.
|
|
31068
31438
|
graph: _extends({}, graph, {
|
|
31069
31439
|
coords: (_this$angle = this.angle) == null ? void 0 : _this$angle.getClockwiseCoords()
|
|
31070
31440
|
})
|
|
@@ -31656,9 +32026,8 @@ class InteractiveGraph extends React.Component {
|
|
|
31656
32026
|
};
|
|
31657
32027
|
}
|
|
31658
32028
|
} else if (userInput.type === "point" && rubric.correct.type === "point" && userInput.coords != null) {
|
|
31659
|
-
var _userInput$coords;
|
|
31660
32029
|
let correct = InteractiveGraph.getPointCoords(rubric.correct, component);
|
|
31661
|
-
const guess =
|
|
32030
|
+
const guess = userInput.coords.slice();
|
|
31662
32031
|
correct = correct.slice();
|
|
31663
32032
|
// Everything's already rounded so we shouldn't need to do an
|
|
31664
32033
|
// eq() comparison but _.isEqual(0, -0) is false, so we'll use
|
|
@@ -31676,8 +32045,8 @@ class InteractiveGraph extends React.Component {
|
|
|
31676
32045
|
};
|
|
31677
32046
|
}
|
|
31678
32047
|
} else if (userInput.type === "polygon" && rubric.correct.type === "polygon" && userInput.coords != null) {
|
|
31679
|
-
var _userInput$
|
|
31680
|
-
const guess = (_userInput$
|
|
32048
|
+
var _userInput$coords, _rubric$correct$coord;
|
|
32049
|
+
const guess = (_userInput$coords = userInput.coords) == null ? void 0 : _userInput$coords.slice();
|
|
31681
32050
|
// @ts-expect-error - TS2322 - Type 'Coord[] | undefined' is not assignable to type 'Coord[]'.
|
|
31682
32051
|
const correct = (_rubric$correct$coord = rubric.correct.coords) == null ? void 0 : _rubric$correct$coord.slice();
|
|
31683
32052
|
let match;
|
|
@@ -31702,9 +32071,9 @@ class InteractiveGraph extends React.Component {
|
|
|
31702
32071
|
};
|
|
31703
32072
|
}
|
|
31704
32073
|
} else if (userInput.type === "segment" && rubric.correct.type === "segment" && userInput.coords != null) {
|
|
31705
|
-
var _rubric$correct
|
|
31706
|
-
let guess = userInput.coords
|
|
31707
|
-
let correct = (_rubric$correct
|
|
32074
|
+
var _rubric$correct;
|
|
32075
|
+
let guess = Util.deepClone(userInput.coords);
|
|
32076
|
+
let correct = Util.deepClone((_rubric$correct = rubric.correct) == null ? void 0 : _rubric$correct.coords);
|
|
31708
32077
|
guess = _.invoke(guess, "sort").sort();
|
|
31709
32078
|
// @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
31710
32079
|
correct = _.invoke(correct, "sort").sort();
|
|
@@ -41972,7 +42341,7 @@ const init = function init(options) {
|
|
|
41972
42341
|
|
|
41973
42342
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
41974
42343
|
const libName = "@khanacademy/perseus";
|
|
41975
|
-
const libVersion = "21.
|
|
42344
|
+
const libVersion = "21.2.0";
|
|
41976
42345
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
41977
42346
|
|
|
41978
42347
|
const apiVersion = {
|
|
@@ -43096,6 +43465,9 @@ const ItemExtras = [
|
|
|
43096
43465
|
// A background image applied to various widgets.
|
|
43097
43466
|
|
|
43098
43467
|
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
43468
|
+
|
|
43469
|
+
// 2D range: xMin, xMax, yMin, yMax
|
|
43470
|
+
|
|
43099
43471
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
43100
43472
|
|
|
43101
43473
|
/**
|
|
@@ -44482,7 +44854,7 @@ function getAnswersFromWidgets(widgets) {
|
|
|
44482
44854
|
// E.g. 'item 1\nitem 2\nitem 3'
|
|
44483
44855
|
const orderer = widget;
|
|
44484
44856
|
if ((_orderer$options = orderer.options) != null && _orderer$options.correctOptions) {
|
|
44485
|
-
answers.push(orderer.options.correctOptions
|
|
44857
|
+
answers.push(joinOptionContents(orderer.options.correctOptions));
|
|
44486
44858
|
}
|
|
44487
44859
|
break;
|
|
44488
44860
|
case "sorter":
|
|
@@ -44541,6 +44913,28 @@ function getAnswersFromWidgets(widgets) {
|
|
|
44541
44913
|
return answers;
|
|
44542
44914
|
}
|
|
44543
44915
|
|
|
44916
|
+
/**
|
|
44917
|
+
* Join the content of the options together.
|
|
44918
|
+
* @param {Array<{content: string}>} options
|
|
44919
|
+
* @returns {string}
|
|
44920
|
+
*/
|
|
44921
|
+
const joinOptionContents = options => options.map(({
|
|
44922
|
+
content
|
|
44923
|
+
}) => content).join("\n");
|
|
44924
|
+
|
|
44925
|
+
/**
|
|
44926
|
+
* Convert an index to an option letter.
|
|
44927
|
+
* @param {number} index
|
|
44928
|
+
* @returns {string}
|
|
44929
|
+
* @example
|
|
44930
|
+
* toOptionLetter(0) // 'A'
|
|
44931
|
+
* toOptionLetter(1) // 'B'
|
|
44932
|
+
* toOptionLetter(25) // 'Z'
|
|
44933
|
+
* // Once the index goes past 25, it will start returning special characters
|
|
44934
|
+
* toOptionLetter(26) // '['
|
|
44935
|
+
*/
|
|
44936
|
+
const toOptionLetter = index => String.fromCharCode("A".charCodeAt(0) + index);
|
|
44937
|
+
|
|
44544
44938
|
/**
|
|
44545
44939
|
* Inject a string equivalent of the widgets into the content.
|
|
44546
44940
|
*
|
|
@@ -44551,7 +44945,7 @@ function getAnswersFromWidgets(widgets) {
|
|
|
44551
44945
|
* @param {PerseusRenderer["widgets"]} widgets
|
|
44552
44946
|
* @returns
|
|
44553
44947
|
*/
|
|
44554
|
-
function injectWidgets(content, widgets) {
|
|
44948
|
+
function injectWidgets(content, widgets, widgetProps) {
|
|
44555
44949
|
// The types for taskProgress.itemData are not well defined,
|
|
44556
44950
|
// so there is a chance that widgets or content could be undefined.
|
|
44557
44951
|
// In that scenario, simply exit early and return the content as is.
|
|
@@ -44571,12 +44965,20 @@ function injectWidgets(content, widgets) {
|
|
|
44571
44965
|
switch (widget.type) {
|
|
44572
44966
|
case "radio":
|
|
44573
44967
|
// Replace radio with the radio options
|
|
44574
|
-
// '[[☃ Radio 1]]' -> '
|
|
44968
|
+
// '[[☃ Radio 1]]' -> 'choice 1\nchoice 2\nchoice 3'
|
|
44969
|
+
// or if the current widget state is available with proper order,
|
|
44970
|
+
// '[[☃ Radio 1]]' ->
|
|
44971
|
+
// 'Option A: choice 1\nOption B: choice 2\nOption C: choice 3'
|
|
44575
44972
|
const radio = widget;
|
|
44973
|
+
const radioProps = widgetProps == null ? void 0 : widgetProps[widgetID];
|
|
44576
44974
|
if ((_radio$options = radio.options) != null && (_radio$options = _radio$options.choices) != null && _radio$options.length) {
|
|
44577
44975
|
var _radio$options2;
|
|
44578
|
-
let radioContext =
|
|
44579
|
-
|
|
44976
|
+
let radioContext = joinOptionContents(radioProps ? radioProps.choices.map(({
|
|
44977
|
+
content
|
|
44978
|
+
}, i) => ({
|
|
44979
|
+
content: `Option ${toOptionLetter(i)}: ${content}`
|
|
44980
|
+
})) : radio.options.choices);
|
|
44981
|
+
if (!radioProps && (_radio$options2 = radio.options) != null && _radio$options2.randomize) {
|
|
44580
44982
|
radioContext += "\nThose options are displayed in a different order to the user. If the user says the letter, number, or ordinal number, always ask them clarify which option they are referring to.\n";
|
|
44581
44983
|
}
|
|
44582
44984
|
context = context.replace(`[[☃ ${widgetID}]]`, radioContext);
|
|
@@ -44685,7 +45087,7 @@ function injectWidgets(content, widgets) {
|
|
|
44685
45087
|
// '[[☃ Orderer 1]]' -> 'option 1\noption 2\noption 3'
|
|
44686
45088
|
const orderer = widget;
|
|
44687
45089
|
if ((_orderer$options2 = orderer.options) != null && _orderer$options2.options) {
|
|
44688
|
-
context = context.replace(`[[☃ ${widgetID}]]`, orderer.options.options
|
|
45090
|
+
context = context.replace(`[[☃ ${widgetID}]]`, joinOptionContents(orderer.options.options));
|
|
44689
45091
|
}
|
|
44690
45092
|
break;
|
|
44691
45093
|
case "sorter":
|