@khanacademy/perseus 20.6.0 → 20.8.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 +19 -6
- package/dist/es/index.js +689 -248
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +19 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.js +805 -340
- package/dist/index.js.map +1 -1
- package/dist/util/snowman-utils.d.ts +90 -0
- package/dist/widgets/interactive-graphs/graphs/index.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/polygon.d.ts +5 -0
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +27 -0
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-reducer.d.ts +3 -0
- package/dist/widgets/interactive-graphs/{interactive-graph-state.d.ts → reducer/interactive-graph-state.d.ts} +2 -2
- package/dist/widgets/interactive-graphs/types.d.ts +12 -6
- package/dist/widgets/interactive-graphs/utils.d.ts +5 -0
- package/package.json +3 -5
- package/dist/widgets/interactive-graphs/interactive-graph-action.d.ts +0 -17
- package/dist/widgets/interactive-graphs/interactive-graph-reducer.d.ts +0 -3
package/dist/index.js
CHANGED
|
@@ -18894,7 +18894,7 @@ const isAccessible = widgetInfo => {
|
|
|
18894
18894
|
}
|
|
18895
18895
|
return !!accessible;
|
|
18896
18896
|
};
|
|
18897
|
-
const getAllWidgetTypes = () => {
|
|
18897
|
+
const getAllWidgetTypes$1 = () => {
|
|
18898
18898
|
return ___default["default"].keys(widgets);
|
|
18899
18899
|
};
|
|
18900
18900
|
const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
|
|
@@ -19166,7 +19166,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
19166
19166
|
getVersionVector: getVersionVector,
|
|
19167
19167
|
getPublicWidgets: getPublicWidgets,
|
|
19168
19168
|
isAccessible: isAccessible,
|
|
19169
|
-
getAllWidgetTypes: getAllWidgetTypes,
|
|
19169
|
+
getAllWidgetTypes: getAllWidgetTypes$1,
|
|
19170
19170
|
upgradeWidgetInfoToLatestVersion: upgradeWidgetInfoToLatestVersion,
|
|
19171
19171
|
getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
|
|
19172
19172
|
traverseChildWidgets: traverseChildWidgets$3,
|
|
@@ -28320,22 +28320,55 @@ const GraphLockedLayer = props => {
|
|
|
28320
28320
|
};
|
|
28321
28321
|
|
|
28322
28322
|
const MOVE_CONTROL_POINT = "move-control-point";
|
|
28323
|
-
function moveControlPoint(
|
|
28323
|
+
function moveControlPoint(pointIndex, destination, itemIndex) {
|
|
28324
28324
|
return {
|
|
28325
28325
|
type: MOVE_CONTROL_POINT,
|
|
28326
|
-
|
|
28326
|
+
itemIndex,
|
|
28327
28327
|
pointIndex,
|
|
28328
28328
|
destination
|
|
28329
28329
|
};
|
|
28330
28330
|
}
|
|
28331
|
+
const MOVE_ALL = "move-all";
|
|
28331
28332
|
const MOVE_LINE = "move-line";
|
|
28332
|
-
|
|
28333
|
+
/** This action assumes the state.coords holds an array of collinear tuples that define lines */
|
|
28334
|
+
function moveLine(itemIndex, delta) {
|
|
28333
28335
|
return {
|
|
28334
28336
|
type: MOVE_LINE,
|
|
28335
|
-
|
|
28337
|
+
itemIndex,
|
|
28336
28338
|
delta
|
|
28337
28339
|
};
|
|
28338
28340
|
}
|
|
28341
|
+
/** This action assumes a flat array of vectors */
|
|
28342
|
+
const moveAll = delta => ({
|
|
28343
|
+
type: MOVE_ALL,
|
|
28344
|
+
delta
|
|
28345
|
+
});
|
|
28346
|
+
|
|
28347
|
+
/**
|
|
28348
|
+
* 44 is touch best practice and AAA compliant for WCAG
|
|
28349
|
+
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
28350
|
+
*/
|
|
28351
|
+
const TARGET_SIZE = 44;
|
|
28352
|
+
|
|
28353
|
+
// same as pointsFromNormalized in interactive-graph.tsx
|
|
28354
|
+
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
28355
|
+
// maps over [x, y]
|
|
28356
|
+
coords.map((coord, i) => {
|
|
28357
|
+
const axisRange = range[i];
|
|
28358
|
+
if (noSnap) {
|
|
28359
|
+
return axisRange[0] + (axisRange[1] - axisRange[0]) * coord;
|
|
28360
|
+
}
|
|
28361
|
+
const axisStep = step[i];
|
|
28362
|
+
const nSteps = Math.floor((axisRange[1] - axisRange[0]) / axisStep);
|
|
28363
|
+
const tick = Math.round(coord * nSteps);
|
|
28364
|
+
return axisRange[0] + axisStep * tick;
|
|
28365
|
+
}));
|
|
28366
|
+
|
|
28367
|
+
// same as normalizeCoords in interactive-graph.tsx
|
|
28368
|
+
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
28369
|
+
const extent = ranges[i][1] - ranges[i][0];
|
|
28370
|
+
return (coord + ranges[i][1]) / extent;
|
|
28371
|
+
}));
|
|
28339
28372
|
|
|
28340
28373
|
const defaultStroke = "var(--movable-line-stroke-color)";
|
|
28341
28374
|
const MovableLine = props => {
|
|
@@ -28383,7 +28416,7 @@ const MovableLine = props => {
|
|
|
28383
28416
|
end: endPtPx,
|
|
28384
28417
|
style: {
|
|
28385
28418
|
stroke: "transparent",
|
|
28386
|
-
strokeWidth:
|
|
28419
|
+
strokeWidth: TARGET_SIZE
|
|
28387
28420
|
}
|
|
28388
28421
|
}), /*#__PURE__*/React__namespace.createElement(SVGLine, {
|
|
28389
28422
|
start: startPtPx,
|
|
@@ -28416,7 +28449,7 @@ function SVGLine(props) {
|
|
|
28416
28449
|
x2: end[0],
|
|
28417
28450
|
y2: end[1],
|
|
28418
28451
|
style: style,
|
|
28419
|
-
className: dragging ? "movable-
|
|
28452
|
+
className: dragging ? "movable-dragging" : undefined
|
|
28420
28453
|
});
|
|
28421
28454
|
}
|
|
28422
28455
|
|
|
@@ -28491,7 +28524,7 @@ const StyledMovablePoint = props => {
|
|
|
28491
28524
|
}
|
|
28492
28525
|
}, /*#__PURE__*/React__namespace.createElement("circle", {
|
|
28493
28526
|
className: "movable-point-hitbox",
|
|
28494
|
-
r:
|
|
28527
|
+
r: TARGET_SIZE / 2,
|
|
28495
28528
|
cx: x,
|
|
28496
28529
|
cy: y
|
|
28497
28530
|
}), /*#__PURE__*/React__namespace.createElement("circle", {
|
|
@@ -28527,9 +28560,9 @@ const SegmentGraph = props => {
|
|
|
28527
28560
|
snaps: snapStep,
|
|
28528
28561
|
range: range,
|
|
28529
28562
|
onMoveLine: delta => {
|
|
28530
|
-
dispatch(
|
|
28563
|
+
dispatch(moveLine(i, delta));
|
|
28531
28564
|
},
|
|
28532
|
-
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(
|
|
28565
|
+
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
28533
28566
|
"data-testid": "segment" + i
|
|
28534
28567
|
})));
|
|
28535
28568
|
};
|
|
@@ -28572,9 +28605,9 @@ const LinearGraph = props => {
|
|
|
28572
28605
|
snaps: snapStep,
|
|
28573
28606
|
range: range,
|
|
28574
28607
|
onMoveLine: delta => {
|
|
28575
|
-
dispatch(
|
|
28608
|
+
dispatch(moveLine(i, delta));
|
|
28576
28609
|
},
|
|
28577
|
-
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(
|
|
28610
|
+
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i))
|
|
28578
28611
|
// "linear" or "linear-system" + index
|
|
28579
28612
|
,
|
|
28580
28613
|
"data-testid": type + i,
|
|
@@ -28625,8 +28658,8 @@ const RayGraph = props => {
|
|
|
28625
28658
|
|
|
28626
28659
|
// a ray only has one line
|
|
28627
28660
|
const [start, end] = lines[0];
|
|
28628
|
-
const handleMoveLine = delta => dispatch(
|
|
28629
|
-
const handleMovePoint = (newPoint, pointIndex) => dispatch(moveControlPoint(
|
|
28661
|
+
const handleMoveLine = delta => dispatch(moveLine(0, delta));
|
|
28662
|
+
const handleMovePoint = (newPoint, pointIndex) => dispatch(moveControlPoint(pointIndex, newPoint, 0));
|
|
28630
28663
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(MovableLine, {
|
|
28631
28664
|
start: start,
|
|
28632
28665
|
end: end,
|
|
@@ -28645,6 +28678,62 @@ const RayGraph = props => {
|
|
|
28645
28678
|
}));
|
|
28646
28679
|
};
|
|
28647
28680
|
|
|
28681
|
+
const PolygonGraph = props => {
|
|
28682
|
+
const [focused, setFocused] = React__namespace.useState(false);
|
|
28683
|
+
const [hovered, setHovered] = React__namespace.useState(false);
|
|
28684
|
+
const {
|
|
28685
|
+
dispatch
|
|
28686
|
+
} = props;
|
|
28687
|
+
const {
|
|
28688
|
+
coords,
|
|
28689
|
+
type
|
|
28690
|
+
} = props.graphState;
|
|
28691
|
+
const points = coords ?? [[0, 0]];
|
|
28692
|
+
const pointsSum = points.reduce((acc, point) => mafs.vec.add(acc, point), [0, 0]);
|
|
28693
|
+
const midpoint = pointsSum && mafs.vec.scale(pointsSum, 1 / (points.length ?? 1));
|
|
28694
|
+
const ref = React__namespace.useRef(null);
|
|
28695
|
+
const {
|
|
28696
|
+
dragging
|
|
28697
|
+
} = mafs.useMovable({
|
|
28698
|
+
gestureTarget: ref,
|
|
28699
|
+
point: midpoint,
|
|
28700
|
+
onMove: newPoint => {
|
|
28701
|
+
const delta = mafs.vec.sub(newPoint, midpoint);
|
|
28702
|
+
dispatch(moveAll(delta));
|
|
28703
|
+
},
|
|
28704
|
+
constrain: p => p
|
|
28705
|
+
});
|
|
28706
|
+
const active = hovered || focused || dragging;
|
|
28707
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
28708
|
+
points: [...points],
|
|
28709
|
+
color: "var(--movable-line-stroke-color)",
|
|
28710
|
+
svgPolygonProps: {
|
|
28711
|
+
strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)"
|
|
28712
|
+
}
|
|
28713
|
+
}), /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
28714
|
+
points: [...points],
|
|
28715
|
+
color: "transparent",
|
|
28716
|
+
svgPolygonProps: {
|
|
28717
|
+
ref,
|
|
28718
|
+
tabIndex: 0,
|
|
28719
|
+
strokeWidth: TARGET_SIZE,
|
|
28720
|
+
style: {
|
|
28721
|
+
cursor: dragging ? "grabbing" : "grab"
|
|
28722
|
+
},
|
|
28723
|
+
onFocus: () => setFocused(true),
|
|
28724
|
+
onBlur: () => setFocused(false),
|
|
28725
|
+
onMouseEnter: () => setHovered(true),
|
|
28726
|
+
onMouseLeave: () => setHovered(false),
|
|
28727
|
+
className: "movable-polygon"
|
|
28728
|
+
}
|
|
28729
|
+
}), points.map((point, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
28730
|
+
key: i,
|
|
28731
|
+
point: point,
|
|
28732
|
+
onMove: destination => dispatch(moveControlPoint(i, destination)),
|
|
28733
|
+
"data-testid": type + i
|
|
28734
|
+
})));
|
|
28735
|
+
};
|
|
28736
|
+
|
|
28648
28737
|
const renderLineLabel = (n, _ref) => {
|
|
28649
28738
|
let [min, max] = _ref;
|
|
28650
28739
|
return n !== -1 && n !== min && n !== max;
|
|
@@ -28660,234 +28749,6 @@ const Grid = props => props.markings === "none" ? null : /*#__PURE__*/React__nam
|
|
|
28660
28749
|
yAxis: axisOptions(props, 1)
|
|
28661
28750
|
});
|
|
28662
28751
|
|
|
28663
|
-
function interactiveGraphReducer(state, action) {
|
|
28664
|
-
switch (action.type) {
|
|
28665
|
-
case MOVE_CONTROL_POINT:
|
|
28666
|
-
{
|
|
28667
|
-
const newCoords = updateAtIndex({
|
|
28668
|
-
array: state.coords,
|
|
28669
|
-
index: action.objectIndex,
|
|
28670
|
-
update: tuple => setAtIndex({
|
|
28671
|
-
array: tuple,
|
|
28672
|
-
index: action.pointIndex,
|
|
28673
|
-
newValue: snap(state, bound$1(state, action.destination))
|
|
28674
|
-
})
|
|
28675
|
-
});
|
|
28676
|
-
if (!validSegments(newCoords)) {
|
|
28677
|
-
return state;
|
|
28678
|
-
}
|
|
28679
|
-
return {
|
|
28680
|
-
...state,
|
|
28681
|
-
hasBeenInteractedWith: true,
|
|
28682
|
-
coords: newCoords
|
|
28683
|
-
};
|
|
28684
|
-
}
|
|
28685
|
-
case MOVE_LINE:
|
|
28686
|
-
{
|
|
28687
|
-
const currentLine = state.coords?.[action.lineIndex];
|
|
28688
|
-
if (!currentLine) {
|
|
28689
|
-
throw new Error("No line to move");
|
|
28690
|
-
}
|
|
28691
|
-
const maxMoves = currentLine.map(point => maxMove(state, point));
|
|
28692
|
-
const minMoves = currentLine.map(point => minMove(state, point));
|
|
28693
|
-
const maxXMove = Math.min(...maxMoves.map(move => move[0]));
|
|
28694
|
-
const maxYMove = Math.min(...maxMoves.map(move => move[1]));
|
|
28695
|
-
const minXMove = Math.max(...minMoves.map(move => move[0]));
|
|
28696
|
-
const minYMove = Math.max(...minMoves.map(move => move[1]));
|
|
28697
|
-
const dx = clamp(action.delta[0], minXMove, maxXMove);
|
|
28698
|
-
const dy = clamp(action.delta[1], minYMove, maxYMove);
|
|
28699
|
-
const newValue = [snap(state, mafs.vec.add(currentLine[0], [dx, dy])), snap(state, mafs.vec.add(currentLine[1], [dx, dy]))];
|
|
28700
|
-
const newLine = setAtIndex({
|
|
28701
|
-
array: state.coords,
|
|
28702
|
-
index: action.lineIndex,
|
|
28703
|
-
newValue
|
|
28704
|
-
});
|
|
28705
|
-
return {
|
|
28706
|
-
...state,
|
|
28707
|
-
hasBeenInteractedWith: true,
|
|
28708
|
-
coords: newLine
|
|
28709
|
-
};
|
|
28710
|
-
}
|
|
28711
|
-
default:
|
|
28712
|
-
throw new wonderStuffCore.UnreachableCaseError(action);
|
|
28713
|
-
}
|
|
28714
|
-
}
|
|
28715
|
-
function snap(state, point) {
|
|
28716
|
-
const [requestedX, requestedY] = point;
|
|
28717
|
-
const [snapX, snapY] = state.snapStep;
|
|
28718
|
-
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
28719
|
-
}
|
|
28720
|
-
|
|
28721
|
-
// Returns the closest point to the given `point` that is within the graph
|
|
28722
|
-
// bounds given in `state`.
|
|
28723
|
-
function bound$1(state, point) {
|
|
28724
|
-
const [requestedX, requestedY] = point;
|
|
28725
|
-
const [snapX, snapY] = state.snapStep;
|
|
28726
|
-
const [[minX, maxX], [minY, maxY]] = state.range;
|
|
28727
|
-
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
28728
|
-
}
|
|
28729
|
-
|
|
28730
|
-
// Returns the vector from the given point to the top-right corner of the graph
|
|
28731
|
-
function maxMove(state, point) {
|
|
28732
|
-
const topRight = bound$1(state, [Infinity, Infinity]);
|
|
28733
|
-
return mafs.vec.sub(topRight, point);
|
|
28734
|
-
}
|
|
28735
|
-
|
|
28736
|
-
// Returns the vector from the given point to the bottom-left corner of the
|
|
28737
|
-
// graph
|
|
28738
|
-
function minMove(state, point) {
|
|
28739
|
-
const bottomLeft = bound$1(state, [-Infinity, -Infinity]);
|
|
28740
|
-
return mafs.vec.sub(bottomLeft, point);
|
|
28741
|
-
}
|
|
28742
|
-
function clamp(value, min, max) {
|
|
28743
|
-
if (value < min) {
|
|
28744
|
-
return min;
|
|
28745
|
-
}
|
|
28746
|
-
if (value > max) {
|
|
28747
|
-
return max;
|
|
28748
|
-
}
|
|
28749
|
-
return value;
|
|
28750
|
-
}
|
|
28751
|
-
function validSegments(segments) {
|
|
28752
|
-
return segments.every(_ref => {
|
|
28753
|
-
let [start, end] = _ref;
|
|
28754
|
-
return !kmath.vector.equal(start, end);
|
|
28755
|
-
});
|
|
28756
|
-
}
|
|
28757
|
-
function updateAtIndex(args) {
|
|
28758
|
-
const {
|
|
28759
|
-
array = [],
|
|
28760
|
-
index,
|
|
28761
|
-
update
|
|
28762
|
-
} = args;
|
|
28763
|
-
const newValue = update(array[index]);
|
|
28764
|
-
return setAtIndex({
|
|
28765
|
-
array,
|
|
28766
|
-
index,
|
|
28767
|
-
newValue
|
|
28768
|
-
});
|
|
28769
|
-
}
|
|
28770
|
-
function setAtIndex(args) {
|
|
28771
|
-
const {
|
|
28772
|
-
array = [],
|
|
28773
|
-
index,
|
|
28774
|
-
newValue
|
|
28775
|
-
} = args;
|
|
28776
|
-
const copy = [...array];
|
|
28777
|
-
copy[index] = newValue;
|
|
28778
|
-
// restoring readonly to array
|
|
28779
|
-
return copy;
|
|
28780
|
-
}
|
|
28781
|
-
|
|
28782
|
-
// same as pointsFromNormalized in interactive-graph.tsx
|
|
28783
|
-
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
28784
|
-
// maps over [x, y]
|
|
28785
|
-
coords.map((coord, i) => {
|
|
28786
|
-
const axisRange = range[i];
|
|
28787
|
-
if (noSnap) {
|
|
28788
|
-
return axisRange[0] + (axisRange[1] - axisRange[0]) * coord;
|
|
28789
|
-
}
|
|
28790
|
-
const axisStep = step[i];
|
|
28791
|
-
const nSteps = Math.floor((axisRange[1] - axisRange[0]) / axisStep);
|
|
28792
|
-
const tick = Math.round(coord * nSteps);
|
|
28793
|
-
return axisRange[0] + axisStep * tick;
|
|
28794
|
-
}));
|
|
28795
|
-
|
|
28796
|
-
// same as normalizeCoords in interactive-graph.tsx
|
|
28797
|
-
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
28798
|
-
const extent = ranges[i][1] - ranges[i][0];
|
|
28799
|
-
return (coord + ranges[i][1]) / extent;
|
|
28800
|
-
}));
|
|
28801
|
-
|
|
28802
|
-
function initializeGraphState(params) {
|
|
28803
|
-
const {
|
|
28804
|
-
graph,
|
|
28805
|
-
step,
|
|
28806
|
-
snapStep,
|
|
28807
|
-
range
|
|
28808
|
-
} = params;
|
|
28809
|
-
switch (graph.type) {
|
|
28810
|
-
case "segment":
|
|
28811
|
-
return {
|
|
28812
|
-
type: "segment",
|
|
28813
|
-
hasBeenInteractedWith: false,
|
|
28814
|
-
range,
|
|
28815
|
-
snapStep,
|
|
28816
|
-
coords: getDefaultSegments({
|
|
28817
|
-
graph,
|
|
28818
|
-
step,
|
|
28819
|
-
range
|
|
28820
|
-
})
|
|
28821
|
-
};
|
|
28822
|
-
case "linear":
|
|
28823
|
-
case "linear-system":
|
|
28824
|
-
case "ray":
|
|
28825
|
-
return {
|
|
28826
|
-
type: graph.type,
|
|
28827
|
-
hasBeenInteractedWith: false,
|
|
28828
|
-
range,
|
|
28829
|
-
snapStep,
|
|
28830
|
-
// Linear and ray graphs have a single tuple of points, while a
|
|
28831
|
-
// linear system has two tuples of points.
|
|
28832
|
-
coords: getLineCoords(graph, range, step)
|
|
28833
|
-
};
|
|
28834
|
-
}
|
|
28835
|
-
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
28836
|
-
}
|
|
28837
|
-
const getDefaultSegments = props => {
|
|
28838
|
-
const ys = n => {
|
|
28839
|
-
switch (n) {
|
|
28840
|
-
case 2:
|
|
28841
|
-
return [5, -5];
|
|
28842
|
-
case 3:
|
|
28843
|
-
return [5, 0, -5];
|
|
28844
|
-
case 4:
|
|
28845
|
-
return [6, 2, -2, -6];
|
|
28846
|
-
case 5:
|
|
28847
|
-
return [6, 3, 0, -3, -6];
|
|
28848
|
-
case 6:
|
|
28849
|
-
return [5, 3, 1, -1, -3, -5];
|
|
28850
|
-
default:
|
|
28851
|
-
return [5];
|
|
28852
|
-
}
|
|
28853
|
-
};
|
|
28854
|
-
return ys(props.graph.numSegments).map(y => {
|
|
28855
|
-
let endpoints = [[-5, y], [5, y]];
|
|
28856
|
-
endpoints = normalizeCoords(endpoints, props.range);
|
|
28857
|
-
endpoints = normalizePoints(props.range, props.step, endpoints);
|
|
28858
|
-
return endpoints;
|
|
28859
|
-
});
|
|
28860
|
-
};
|
|
28861
|
-
|
|
28862
|
-
// TS v4 doesn't narrow return types, while v5 does.
|
|
28863
|
-
// Instead of updating to v5, using generic type to relate input and output types.
|
|
28864
|
-
function getGradableGraph(state, initialGraph) {
|
|
28865
|
-
if (!state.hasBeenInteractedWith) {
|
|
28866
|
-
return initialGraph;
|
|
28867
|
-
}
|
|
28868
|
-
switch (true) {
|
|
28869
|
-
// coords: Array of CollinearTuple
|
|
28870
|
-
case state.type === "linear-system" && initialGraph.type === "linear-system":
|
|
28871
|
-
case state.type === "segment" && initialGraph.type === "segment":
|
|
28872
|
-
return {
|
|
28873
|
-
...initialGraph,
|
|
28874
|
-
coords: state.coords
|
|
28875
|
-
};
|
|
28876
|
-
// coords: CollinearTuple
|
|
28877
|
-
case state.type === "linear" && initialGraph.type === "linear":
|
|
28878
|
-
case state.type === "ray" && initialGraph.type === "ray":
|
|
28879
|
-
return {
|
|
28880
|
-
...initialGraph,
|
|
28881
|
-
coords: state.coords?.[0]
|
|
28882
|
-
};
|
|
28883
|
-
}
|
|
28884
|
-
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
28885
|
-
}
|
|
28886
|
-
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
28887
|
-
const getLineCoords = (graph, range, step) =>
|
|
28888
|
-
// Return two lines for a linear system, one for a ray or linear
|
|
28889
|
-
graph.coords ?? graph.type === "linear-system" ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])];
|
|
28890
|
-
|
|
28891
28752
|
// Displays a stylized open eye: lint warnings are visible
|
|
28892
28753
|
const VisibleIcon = () => /*#__PURE__*/React__namespace.createElement("svg", {
|
|
28893
28754
|
width: "24",
|
|
@@ -29477,107 +29338,486 @@ const styles$h = aphrodite.StyleSheet.create({
|
|
|
29477
29338
|
flexDirection: "row",
|
|
29478
29339
|
alignItems: "center"
|
|
29479
29340
|
}
|
|
29480
|
-
});
|
|
29481
|
-
|
|
29482
|
-
const truth = () => true;
|
|
29341
|
+
});
|
|
29342
|
+
|
|
29343
|
+
const truth = () => true;
|
|
29344
|
+
|
|
29345
|
+
/* A minor abstraction on top of NumberInput for ranges
|
|
29346
|
+
*
|
|
29347
|
+
*/
|
|
29348
|
+
class RangeInput extends React__namespace.Component {
|
|
29349
|
+
static propTypes = {
|
|
29350
|
+
value: PropTypes__default["default"].array.isRequired,
|
|
29351
|
+
onChange: PropTypes__default["default"].func.isRequired,
|
|
29352
|
+
placeholder: PropTypes__default["default"].array,
|
|
29353
|
+
checkValidity: PropTypes__default["default"].func
|
|
29354
|
+
};
|
|
29355
|
+
static defaultProps = {
|
|
29356
|
+
placeholder: [null, null]
|
|
29357
|
+
};
|
|
29358
|
+
render() {
|
|
29359
|
+
const value = this.props.value;
|
|
29360
|
+
const checkValidity = this.props.checkValidity || truth;
|
|
29361
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
29362
|
+
className: "range-input"
|
|
29363
|
+
}, /*#__PURE__*/React__namespace.createElement(NumberInput, _extends({}, this.props, {
|
|
29364
|
+
value: value[0],
|
|
29365
|
+
checkValidity: val => checkValidity([val, value[1]])
|
|
29366
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
29367
|
+
,
|
|
29368
|
+
onChange: this.onChange.bind(this, 0),
|
|
29369
|
+
placeholder: this.props.placeholder[0]
|
|
29370
|
+
})), /*#__PURE__*/React__namespace.createElement(NumberInput, _extends({}, this.props, {
|
|
29371
|
+
value: value[1],
|
|
29372
|
+
checkValidity: val => checkValidity([value[0], val])
|
|
29373
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
29374
|
+
,
|
|
29375
|
+
onChange: this.onChange.bind(this, 1),
|
|
29376
|
+
placeholder: this.props.placeholder[1]
|
|
29377
|
+
})));
|
|
29378
|
+
}
|
|
29379
|
+
onChange = (i, newVal) => {
|
|
29380
|
+
const value = this.props.value;
|
|
29381
|
+
if (i === 0) {
|
|
29382
|
+
this.props.onChange([newVal, value[1]]);
|
|
29383
|
+
} else {
|
|
29384
|
+
this.props.onChange([value[0], newVal]);
|
|
29385
|
+
}
|
|
29386
|
+
};
|
|
29387
|
+
}
|
|
29388
|
+
|
|
29389
|
+
var components = /*#__PURE__*/Object.freeze({
|
|
29390
|
+
__proto__: null,
|
|
29391
|
+
ButtonGroup: ButtonGroup,
|
|
29392
|
+
Graph: Graph,
|
|
29393
|
+
Graphie: Graphie,
|
|
29394
|
+
HUD: HUD,
|
|
29395
|
+
Icon: Icon,
|
|
29396
|
+
InfoTip: InfoTip,
|
|
29397
|
+
InlineIcon: InlineIcon,
|
|
29398
|
+
MathInput: MathInput,
|
|
29399
|
+
MultiButtonGroup: MultiButtonGroup,
|
|
29400
|
+
NumberInput: NumberInput,
|
|
29401
|
+
PropCheckBox: PropCheckBox,
|
|
29402
|
+
RangeInput: RangeInput,
|
|
29403
|
+
SvgImage: SvgImage,
|
|
29404
|
+
TextInput: TextInput,
|
|
29405
|
+
TextListEditor: TextListEditor
|
|
29406
|
+
});
|
|
29407
|
+
|
|
29408
|
+
/**
|
|
29409
|
+
* If a graphie URL is provided in `backgroundImage`, will return the rendered graphie background.
|
|
29410
|
+
* Otherwise, returns `null`.
|
|
29411
|
+
*/
|
|
29412
|
+
const getLegacyGrid = (box, backgroundImage) => {
|
|
29413
|
+
const {
|
|
29414
|
+
url,
|
|
29415
|
+
width,
|
|
29416
|
+
height
|
|
29417
|
+
} = backgroundImage ?? {};
|
|
29418
|
+
if (url && typeof url === "string") {
|
|
29419
|
+
const scale = box[0] / interactiveSizes$1.defaultBoxSize;
|
|
29420
|
+
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
29421
|
+
style: {
|
|
29422
|
+
position: "absolute",
|
|
29423
|
+
bottom: 0,
|
|
29424
|
+
left: 0
|
|
29425
|
+
}
|
|
29426
|
+
}, /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null, _ref => {
|
|
29427
|
+
let {
|
|
29428
|
+
setAssetStatus
|
|
29429
|
+
} = _ref;
|
|
29430
|
+
return /*#__PURE__*/React__namespace.createElement(SvgImage, {
|
|
29431
|
+
src: url,
|
|
29432
|
+
width: width,
|
|
29433
|
+
height: height,
|
|
29434
|
+
scale: scale,
|
|
29435
|
+
responsive: false,
|
|
29436
|
+
setAssetStatus: setAssetStatus,
|
|
29437
|
+
alt: ""
|
|
29438
|
+
});
|
|
29439
|
+
}));
|
|
29440
|
+
}
|
|
29441
|
+
return null;
|
|
29442
|
+
};
|
|
29443
|
+
|
|
29444
|
+
/** Determine if coords is type CollinearTuple[] */
|
|
29445
|
+
const isCollinearTuples = coords => Array.isArray(coords[0][0]);
|
|
29446
|
+
|
|
29447
|
+
// Generic type makes returned state match input state
|
|
29448
|
+
function interactiveGraphReducer(state, action) {
|
|
29449
|
+
const {
|
|
29450
|
+
snapStep,
|
|
29451
|
+
range
|
|
29452
|
+
} = state;
|
|
29453
|
+
if (!state.coords) {
|
|
29454
|
+
throw new Error("Graph state must have been initialized with coords");
|
|
29455
|
+
}
|
|
29456
|
+
switch (action.type) {
|
|
29457
|
+
case MOVE_CONTROL_POINT:
|
|
29458
|
+
{
|
|
29459
|
+
const newCoords = action.itemIndex !== undefined && state.coords && isCollinearTuples(state.coords) ? updateAtIndex({
|
|
29460
|
+
array: state.coords,
|
|
29461
|
+
index: action.itemIndex,
|
|
29462
|
+
update: tuple => setAtIndex({
|
|
29463
|
+
array: tuple,
|
|
29464
|
+
index: action.pointIndex,
|
|
29465
|
+
newValue: snap({
|
|
29466
|
+
snapStep,
|
|
29467
|
+
point: bound$1({
|
|
29468
|
+
snapStep,
|
|
29469
|
+
range,
|
|
29470
|
+
point: action.destination
|
|
29471
|
+
})
|
|
29472
|
+
})
|
|
29473
|
+
})
|
|
29474
|
+
}) : setAtIndex({
|
|
29475
|
+
array: state.coords,
|
|
29476
|
+
index: action.pointIndex,
|
|
29477
|
+
newValue: snap({
|
|
29478
|
+
snapStep,
|
|
29479
|
+
point: bound$1({
|
|
29480
|
+
snapStep,
|
|
29481
|
+
range,
|
|
29482
|
+
point: action.destination
|
|
29483
|
+
})
|
|
29484
|
+
})
|
|
29485
|
+
});
|
|
29486
|
+
|
|
29487
|
+
// Cannot type narrow both conditions within function parameters,
|
|
29488
|
+
// so this may seem redundant, but it's necessary for type safety.
|
|
29489
|
+
const coordsToCheck = isCollinearTuples(newCoords) && action.itemIndex !== undefined ? newCoords[action.itemIndex] : newCoords;
|
|
29490
|
+
if (!isCollinearTuples(coordsToCheck) && coordsOverlap(coordsToCheck)) {
|
|
29491
|
+
return state;
|
|
29492
|
+
}
|
|
29493
|
+
return {
|
|
29494
|
+
...state,
|
|
29495
|
+
hasBeenInteractedWith: true,
|
|
29496
|
+
coords: newCoords
|
|
29497
|
+
};
|
|
29498
|
+
}
|
|
29499
|
+
case MOVE_LINE:
|
|
29500
|
+
{
|
|
29501
|
+
if (!isCollinearTuples(state.coords)) {
|
|
29502
|
+
throw new Error("Cannot call this action unless coords is array of CollinearTuple.");
|
|
29503
|
+
}
|
|
29504
|
+
if (action.itemIndex === undefined) {
|
|
29505
|
+
throw new Error("Please provide index of line to move");
|
|
29506
|
+
}
|
|
29507
|
+
const currentLine = state.coords?.[action.itemIndex];
|
|
29508
|
+
if (!currentLine) {
|
|
29509
|
+
throw new Error("No line to move");
|
|
29510
|
+
}
|
|
29511
|
+
const change = getChange(currentLine, action.delta, {
|
|
29512
|
+
snapStep,
|
|
29513
|
+
range
|
|
29514
|
+
});
|
|
29515
|
+
const newLine = [snap({
|
|
29516
|
+
snapStep,
|
|
29517
|
+
point: mafs.vec.add(currentLine[0], change)
|
|
29518
|
+
}), snap({
|
|
29519
|
+
snapStep,
|
|
29520
|
+
point: mafs.vec.add(currentLine[1], change)
|
|
29521
|
+
})];
|
|
29522
|
+
const newCoords = setAtIndex({
|
|
29523
|
+
array: state.coords,
|
|
29524
|
+
index: action.itemIndex,
|
|
29525
|
+
newValue: newLine
|
|
29526
|
+
});
|
|
29527
|
+
return {
|
|
29528
|
+
...state,
|
|
29529
|
+
hasBeenInteractedWith: true,
|
|
29530
|
+
coords: newCoords
|
|
29531
|
+
};
|
|
29532
|
+
}
|
|
29533
|
+
case MOVE_ALL:
|
|
29534
|
+
{
|
|
29535
|
+
if (isCollinearTuples(state.coords)) {
|
|
29536
|
+
throw new Error("Cannot call this action unless coords is array of vectors.");
|
|
29537
|
+
}
|
|
29538
|
+
const change = getChange(state.coords, action.delta, {
|
|
29539
|
+
snapStep,
|
|
29540
|
+
range
|
|
29541
|
+
});
|
|
29542
|
+
const newCoords = state.coords.map(point => snap({
|
|
29543
|
+
snapStep,
|
|
29544
|
+
point: mafs.vec.add(point, change)
|
|
29545
|
+
}));
|
|
29546
|
+
return {
|
|
29547
|
+
...state,
|
|
29548
|
+
hasBeenInteractedWith: true,
|
|
29549
|
+
coords: newCoords
|
|
29550
|
+
};
|
|
29551
|
+
}
|
|
29552
|
+
default:
|
|
29553
|
+
throw new wonderStuffCore.UnreachableCaseError(action);
|
|
29554
|
+
}
|
|
29555
|
+
}
|
|
29556
|
+
const getChange = (coords, delta, constraintOpts) => {
|
|
29557
|
+
const [deltaX, deltaY] = delta;
|
|
29558
|
+
const maxMoves = coords.map(point => maxMove({
|
|
29559
|
+
...constraintOpts,
|
|
29560
|
+
point
|
|
29561
|
+
}));
|
|
29562
|
+
const minMoves = coords.map(point => minMove({
|
|
29563
|
+
...constraintOpts,
|
|
29564
|
+
point
|
|
29565
|
+
}));
|
|
29566
|
+
const maxXMove = Math.min(...maxMoves.map(move => move[0]));
|
|
29567
|
+
const maxYMove = Math.min(...maxMoves.map(move => move[1]));
|
|
29568
|
+
const minXMove = Math.max(...minMoves.map(move => move[0]));
|
|
29569
|
+
const minYMove = Math.max(...minMoves.map(move => move[1]));
|
|
29570
|
+
const dx = clamp(deltaX, minXMove, maxXMove);
|
|
29571
|
+
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
29572
|
+
return [dx, dy];
|
|
29573
|
+
};
|
|
29574
|
+
function snap(_ref) {
|
|
29575
|
+
let {
|
|
29576
|
+
snapStep,
|
|
29577
|
+
point
|
|
29578
|
+
} = _ref;
|
|
29579
|
+
const [requestedX, requestedY] = point;
|
|
29580
|
+
const [snapX, snapY] = snapStep;
|
|
29581
|
+
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
29582
|
+
}
|
|
29583
|
+
|
|
29584
|
+
// Returns the closest point to the given `point` that is within the graph
|
|
29585
|
+
// bounds given in `state`.
|
|
29586
|
+
function bound$1(_ref2) {
|
|
29587
|
+
let {
|
|
29588
|
+
snapStep,
|
|
29589
|
+
range,
|
|
29590
|
+
point
|
|
29591
|
+
} = _ref2;
|
|
29592
|
+
const [requestedX, requestedY] = point;
|
|
29593
|
+
const [snapX, snapY] = snapStep;
|
|
29594
|
+
const [[minX, maxX], [minY, maxY]] = range;
|
|
29595
|
+
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
29596
|
+
}
|
|
29597
|
+
|
|
29598
|
+
// Returns the vector from the given point to the top-right corner of the graph
|
|
29599
|
+
function maxMove(_ref3) {
|
|
29600
|
+
let {
|
|
29601
|
+
snapStep,
|
|
29602
|
+
range,
|
|
29603
|
+
point
|
|
29604
|
+
} = _ref3;
|
|
29605
|
+
const topRight = bound$1({
|
|
29606
|
+
snapStep,
|
|
29607
|
+
range,
|
|
29608
|
+
point: [Infinity, Infinity]
|
|
29609
|
+
});
|
|
29610
|
+
return mafs.vec.sub(topRight, point);
|
|
29611
|
+
}
|
|
29612
|
+
|
|
29613
|
+
// Returns the vector from the given point to the bottom-left corner of the
|
|
29614
|
+
// graph
|
|
29615
|
+
function minMove(_ref4) {
|
|
29616
|
+
let {
|
|
29617
|
+
snapStep,
|
|
29618
|
+
range,
|
|
29619
|
+
point
|
|
29620
|
+
} = _ref4;
|
|
29621
|
+
const bottomLeft = bound$1({
|
|
29622
|
+
snapStep,
|
|
29623
|
+
range,
|
|
29624
|
+
point: [-Infinity, -Infinity]
|
|
29625
|
+
});
|
|
29626
|
+
return mafs.vec.sub(bottomLeft, point);
|
|
29627
|
+
}
|
|
29628
|
+
function clamp(value, min, max) {
|
|
29629
|
+
if (value < min) {
|
|
29630
|
+
return min;
|
|
29631
|
+
}
|
|
29632
|
+
if (value > max) {
|
|
29633
|
+
return max;
|
|
29634
|
+
}
|
|
29635
|
+
return value;
|
|
29636
|
+
}
|
|
29637
|
+
const coordsOverlap = coords => coords.some((coord, i) => coords.some((c, j) => i !== j && kmath.vector.equal(coord, c)));
|
|
29638
|
+
function updateAtIndex(args) {
|
|
29639
|
+
const {
|
|
29640
|
+
array = [],
|
|
29641
|
+
index,
|
|
29642
|
+
update
|
|
29643
|
+
} = args;
|
|
29644
|
+
const newValue = update(array[index]);
|
|
29645
|
+
return setAtIndex({
|
|
29646
|
+
array,
|
|
29647
|
+
index,
|
|
29648
|
+
newValue
|
|
29649
|
+
});
|
|
29650
|
+
}
|
|
29651
|
+
function setAtIndex(args) {
|
|
29652
|
+
const {
|
|
29653
|
+
array = [],
|
|
29654
|
+
index,
|
|
29655
|
+
newValue
|
|
29656
|
+
} = args;
|
|
29657
|
+
const copy = [...array];
|
|
29658
|
+
copy[index] = newValue;
|
|
29659
|
+
// restoring readonly to array
|
|
29660
|
+
return copy;
|
|
29661
|
+
}
|
|
29662
|
+
|
|
29663
|
+
function initializeGraphState(params) {
|
|
29664
|
+
const {
|
|
29665
|
+
graph,
|
|
29666
|
+
step,
|
|
29667
|
+
snapStep,
|
|
29668
|
+
range
|
|
29669
|
+
} = params;
|
|
29670
|
+
switch (graph.type) {
|
|
29671
|
+
case "segment":
|
|
29672
|
+
return {
|
|
29673
|
+
type: "segment",
|
|
29674
|
+
hasBeenInteractedWith: false,
|
|
29675
|
+
range,
|
|
29676
|
+
snapStep,
|
|
29677
|
+
coords: getDefaultSegments({
|
|
29678
|
+
graph,
|
|
29679
|
+
step,
|
|
29680
|
+
range
|
|
29681
|
+
})
|
|
29682
|
+
};
|
|
29683
|
+
case "linear":
|
|
29684
|
+
case "linear-system":
|
|
29685
|
+
case "ray":
|
|
29686
|
+
return {
|
|
29687
|
+
type: graph.type,
|
|
29688
|
+
hasBeenInteractedWith: false,
|
|
29689
|
+
range,
|
|
29690
|
+
snapStep,
|
|
29691
|
+
// Linear and ray graphs have a single tuple of points, while a
|
|
29692
|
+
// linear system has two tuples of points.
|
|
29693
|
+
coords: getLineCoords({
|
|
29694
|
+
graph,
|
|
29695
|
+
range,
|
|
29696
|
+
step
|
|
29697
|
+
})
|
|
29698
|
+
};
|
|
29699
|
+
case "polygon":
|
|
29700
|
+
return {
|
|
29701
|
+
type: "polygon",
|
|
29702
|
+
hasBeenInteractedWith: false,
|
|
29703
|
+
range,
|
|
29704
|
+
snapStep,
|
|
29705
|
+
coords: getPolygonCoords({
|
|
29706
|
+
graph,
|
|
29707
|
+
range,
|
|
29708
|
+
step
|
|
29709
|
+
})
|
|
29710
|
+
};
|
|
29711
|
+
case "angle":
|
|
29712
|
+
case "point":
|
|
29713
|
+
case "circle":
|
|
29714
|
+
case "sinusoid":
|
|
29715
|
+
case "quadratic":
|
|
29716
|
+
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
29717
|
+
default:
|
|
29718
|
+
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
29719
|
+
}
|
|
29720
|
+
}
|
|
29483
29721
|
|
|
29484
|
-
|
|
29485
|
-
|
|
29486
|
-
|
|
29487
|
-
|
|
29488
|
-
|
|
29489
|
-
value: PropTypes__default["default"].array.isRequired,
|
|
29490
|
-
onChange: PropTypes__default["default"].func.isRequired,
|
|
29491
|
-
placeholder: PropTypes__default["default"].array,
|
|
29492
|
-
checkValidity: PropTypes__default["default"].func
|
|
29493
|
-
};
|
|
29494
|
-
static defaultProps = {
|
|
29495
|
-
placeholder: [null, null]
|
|
29496
|
-
};
|
|
29497
|
-
render() {
|
|
29498
|
-
const value = this.props.value;
|
|
29499
|
-
const checkValidity = this.props.checkValidity || truth;
|
|
29500
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
29501
|
-
className: "range-input"
|
|
29502
|
-
}, /*#__PURE__*/React__namespace.createElement(NumberInput, _extends({}, this.props, {
|
|
29503
|
-
value: value[0],
|
|
29504
|
-
checkValidity: val => checkValidity([val, value[1]])
|
|
29505
|
-
// eslint-disable-next-line react/jsx-no-bind
|
|
29506
|
-
,
|
|
29507
|
-
onChange: this.onChange.bind(this, 0),
|
|
29508
|
-
placeholder: this.props.placeholder[0]
|
|
29509
|
-
})), /*#__PURE__*/React__namespace.createElement(NumberInput, _extends({}, this.props, {
|
|
29510
|
-
value: value[1],
|
|
29511
|
-
checkValidity: val => checkValidity([value[0], val])
|
|
29512
|
-
// eslint-disable-next-line react/jsx-no-bind
|
|
29513
|
-
,
|
|
29514
|
-
onChange: this.onChange.bind(this, 1),
|
|
29515
|
-
placeholder: this.props.placeholder[1]
|
|
29516
|
-
})));
|
|
29722
|
+
// TS v4 doesn't narrow return types, while v5 does.
|
|
29723
|
+
// Instead of updating to v5, using generic type to relate input and output types.
|
|
29724
|
+
function getGradableGraph(state, initialGraph) {
|
|
29725
|
+
if (!state.hasBeenInteractedWith) {
|
|
29726
|
+
return initialGraph;
|
|
29517
29727
|
}
|
|
29518
|
-
|
|
29519
|
-
|
|
29520
|
-
|
|
29521
|
-
|
|
29522
|
-
|
|
29523
|
-
|
|
29728
|
+
switch (true) {
|
|
29729
|
+
// coords: Array of CollinearTuple
|
|
29730
|
+
case state.type === "linear-system" && initialGraph.type === "linear-system":
|
|
29731
|
+
case state.type === "segment" && initialGraph.type === "segment":
|
|
29732
|
+
return {
|
|
29733
|
+
...initialGraph,
|
|
29734
|
+
coords: state.coords
|
|
29735
|
+
};
|
|
29736
|
+
// coords: CollinearTuple
|
|
29737
|
+
case state.type === "linear" && initialGraph.type === "linear":
|
|
29738
|
+
case state.type === "ray" && initialGraph.type === "ray":
|
|
29739
|
+
return {
|
|
29740
|
+
...initialGraph,
|
|
29741
|
+
coords: state.coords?.[0]
|
|
29742
|
+
};
|
|
29743
|
+
case state.type === "polygon" && initialGraph.type === "polygon":
|
|
29744
|
+
return {
|
|
29745
|
+
...initialGraph,
|
|
29746
|
+
coords: state.coords
|
|
29747
|
+
};
|
|
29748
|
+
default:
|
|
29749
|
+
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29750
|
+
}
|
|
29751
|
+
}
|
|
29752
|
+
const getDefaultSegments = _ref => {
|
|
29753
|
+
let {
|
|
29754
|
+
graph,
|
|
29755
|
+
range,
|
|
29756
|
+
step
|
|
29757
|
+
} = _ref;
|
|
29758
|
+
const ys = n => {
|
|
29759
|
+
switch (n) {
|
|
29760
|
+
case 2:
|
|
29761
|
+
return [5, -5];
|
|
29762
|
+
case 3:
|
|
29763
|
+
return [5, 0, -5];
|
|
29764
|
+
case 4:
|
|
29765
|
+
return [6, 2, -2, -6];
|
|
29766
|
+
case 5:
|
|
29767
|
+
return [6, 3, 0, -3, -6];
|
|
29768
|
+
case 6:
|
|
29769
|
+
return [5, 3, 1, -1, -3, -5];
|
|
29770
|
+
default:
|
|
29771
|
+
return [5];
|
|
29524
29772
|
}
|
|
29525
29773
|
};
|
|
29526
|
-
|
|
29774
|
+
return ys(graph.numSegments).map(y => {
|
|
29775
|
+
let endpoints = [[-5, y], [5, y]];
|
|
29776
|
+
endpoints = normalizeCoords(endpoints, range);
|
|
29777
|
+
endpoints = normalizePoints(range, step, endpoints);
|
|
29778
|
+
return endpoints;
|
|
29779
|
+
});
|
|
29780
|
+
};
|
|
29781
|
+
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
29782
|
+
const getLineCoords = _ref2 => {
|
|
29783
|
+
let {
|
|
29784
|
+
graph,
|
|
29785
|
+
range,
|
|
29786
|
+
step
|
|
29787
|
+
} = _ref2;
|
|
29788
|
+
return (
|
|
29789
|
+
// Return two lines for a linear system, one for a ray or linear
|
|
29790
|
+
graph.coords ?? graph.type === "linear-system" ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
29791
|
+
);
|
|
29792
|
+
};
|
|
29793
|
+
const getPolygonCoords = _ref3 => {
|
|
29794
|
+
let {
|
|
29795
|
+
graph,
|
|
29796
|
+
range,
|
|
29797
|
+
step
|
|
29798
|
+
} = _ref3;
|
|
29799
|
+
let coords = graph.coords;
|
|
29800
|
+
if (coords) {
|
|
29801
|
+
return coords;
|
|
29802
|
+
}
|
|
29803
|
+
const n = graph.numSides || 3;
|
|
29804
|
+
if (n === "unlimited") {
|
|
29805
|
+
coords = [];
|
|
29806
|
+
} else {
|
|
29807
|
+
const angle = 2 * Math.PI / n;
|
|
29808
|
+
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
29527
29809
|
|
|
29528
|
-
|
|
29529
|
-
|
|
29530
|
-
|
|
29531
|
-
Graph: Graph,
|
|
29532
|
-
Graphie: Graphie,
|
|
29533
|
-
HUD: HUD,
|
|
29534
|
-
Icon: Icon,
|
|
29535
|
-
InfoTip: InfoTip,
|
|
29536
|
-
InlineIcon: InlineIcon,
|
|
29537
|
-
MathInput: MathInput,
|
|
29538
|
-
MultiButtonGroup: MultiButtonGroup,
|
|
29539
|
-
NumberInput: NumberInput,
|
|
29540
|
-
PropCheckBox: PropCheckBox,
|
|
29541
|
-
RangeInput: RangeInput,
|
|
29542
|
-
SvgImage: SvgImage,
|
|
29543
|
-
TextInput: TextInput,
|
|
29544
|
-
TextListEditor: TextListEditor
|
|
29545
|
-
});
|
|
29810
|
+
// TODO(alex): Generalize this to more than just triangles so that
|
|
29811
|
+
// all polygons have whole number side lengths if snapping to sides
|
|
29812
|
+
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
29546
29813
|
|
|
29547
|
-
|
|
29548
|
-
|
|
29549
|
-
* Otherwise, returns `null`.
|
|
29550
|
-
*/
|
|
29551
|
-
const getLegacyGrid = (box, backgroundImage) => {
|
|
29552
|
-
const {
|
|
29553
|
-
url,
|
|
29554
|
-
width,
|
|
29555
|
-
height
|
|
29556
|
-
} = backgroundImage ?? {};
|
|
29557
|
-
if (url && typeof url === "string") {
|
|
29558
|
-
const scale = box[0] / interactiveSizes$1.defaultBoxSize;
|
|
29559
|
-
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
29560
|
-
style: {
|
|
29561
|
-
position: "absolute",
|
|
29562
|
-
bottom: 0,
|
|
29563
|
-
left: 0
|
|
29564
|
-
}
|
|
29565
|
-
}, /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null, _ref => {
|
|
29566
|
-
let {
|
|
29567
|
-
setAssetStatus
|
|
29568
|
-
} = _ref;
|
|
29569
|
-
return /*#__PURE__*/React__namespace.createElement(SvgImage, {
|
|
29570
|
-
src: url,
|
|
29571
|
-
width: width,
|
|
29572
|
-
height: height,
|
|
29573
|
-
scale: scale,
|
|
29574
|
-
responsive: false,
|
|
29575
|
-
setAssetStatus: setAssetStatus,
|
|
29576
|
-
alt: ""
|
|
29577
|
-
});
|
|
29578
|
-
}));
|
|
29814
|
+
// Generate coords of a regular polygon with n sides
|
|
29815
|
+
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
29579
29816
|
}
|
|
29580
|
-
|
|
29817
|
+
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
29818
|
+
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
29819
|
+
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
29820
|
+
return coords;
|
|
29581
29821
|
};
|
|
29582
29822
|
|
|
29583
29823
|
const renderGraph = props => {
|
|
@@ -29605,6 +29845,11 @@ const renderGraph = props => {
|
|
|
29605
29845
|
graphState: state,
|
|
29606
29846
|
dispatch: dispatch
|
|
29607
29847
|
});
|
|
29848
|
+
case "polygon":
|
|
29849
|
+
return /*#__PURE__*/React__namespace.createElement(PolygonGraph, {
|
|
29850
|
+
graphState: state,
|
|
29851
|
+
dispatch: dispatch
|
|
29852
|
+
});
|
|
29608
29853
|
default:
|
|
29609
29854
|
return new wonderStuffCore.UnreachableCaseError(type);
|
|
29610
29855
|
}
|
|
@@ -29618,8 +29863,8 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
|
29618
29863
|
}));
|
|
29619
29864
|
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
29620
29865
|
style: {
|
|
29621
|
-
width
|
|
29622
|
-
height
|
|
29866
|
+
width,
|
|
29867
|
+
height,
|
|
29623
29868
|
position: "relative"
|
|
29624
29869
|
}
|
|
29625
29870
|
}, legacyGrid, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
@@ -41649,7 +41894,7 @@ const init = function (options) {
|
|
|
41649
41894
|
|
|
41650
41895
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
41651
41896
|
const libName = "@khanacademy/perseus";
|
|
41652
|
-
const libVersion = "20.
|
|
41897
|
+
const libVersion = "20.8.0";
|
|
41653
41898
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
41654
41899
|
|
|
41655
41900
|
const apiVersion = {
|
|
@@ -44297,6 +44542,66 @@ function injectWidgets(content, widgets) {
|
|
|
44297
44542
|
return context;
|
|
44298
44543
|
}
|
|
44299
44544
|
|
|
44545
|
+
/* Widgets that have individual answers */
|
|
44546
|
+
const INDIVIDUAL_ANSWER_WIDGETS = ["interactive-graph", "categorizer", "grapher"];
|
|
44547
|
+
|
|
44548
|
+
/* Widgets that are supported for automatic scoring */
|
|
44549
|
+
const SUPPORTED_WIDGETS = ["radio", "numeric-input", "input-number", "expression", ...INDIVIDUAL_ANSWER_WIDGETS];
|
|
44550
|
+
const getWidgetTypeFromWidgetKey = widgetKey => {
|
|
44551
|
+
return widgetKey.split(" ")[0];
|
|
44552
|
+
};
|
|
44553
|
+
|
|
44554
|
+
/* Verify if the perseus item has supported widgets for automatic scoring */
|
|
44555
|
+
const isWrongAnswerSupported = widgetKeys => {
|
|
44556
|
+
return widgetKeys.length !== 0 && widgetKeys.every(widgetKey => SUPPORTED_WIDGETS.includes(getWidgetTypeFromWidgetKey(widgetKey)));
|
|
44557
|
+
};
|
|
44558
|
+
|
|
44559
|
+
/* Verify if the widget key has an individual answer for the coach report view */
|
|
44560
|
+
const shouldHaveIndividualAnswer = widgetKey => {
|
|
44561
|
+
return INDIVIDUAL_ANSWER_WIDGETS.includes(getWidgetTypeFromWidgetKey(widgetKey));
|
|
44562
|
+
};
|
|
44563
|
+
|
|
44564
|
+
/* Returns the answer userInput submission for currently supported widgets */
|
|
44565
|
+
// TODO (LEMS-1834): Fix userInput any types to be specific
|
|
44566
|
+
const getAnswerFromUserInput = (widgetType, userInput) => {
|
|
44567
|
+
switch (widgetType) {
|
|
44568
|
+
case "categorizer":
|
|
44569
|
+
return userInput.values;
|
|
44570
|
+
case "input-number":
|
|
44571
|
+
return userInput.currentValue;
|
|
44572
|
+
case "numeric-input":
|
|
44573
|
+
return userInput.currentValue;
|
|
44574
|
+
case "radio":
|
|
44575
|
+
return userInput.choicesSelected;
|
|
44576
|
+
}
|
|
44577
|
+
return userInput;
|
|
44578
|
+
};
|
|
44579
|
+
|
|
44580
|
+
/* Returns the correct answer for a given widget key and Perseus Item */
|
|
44581
|
+
// TODO (LEMS-1835): We should fix the resonse type from getWidget to be specific.
|
|
44582
|
+
// TODO (LEMS-1836): We should also consider adding the getOneCorrectAnswerFromRubric method to all widgets.
|
|
44583
|
+
const getCorrectAnswerForWidgetKey = (widgetKey, itemData) => {
|
|
44584
|
+
const rubric = itemData.question.widgets[widgetKey].options;
|
|
44585
|
+
const widgetType = getWidgetTypeFromWidgetKey(widgetKey);
|
|
44586
|
+
const widget = getWidget(widgetType);
|
|
44587
|
+
|
|
44588
|
+
// @ts-expect-error - TS2339 - Property 'getOneCorrectAnswerFromRubric' does not exist on type 'ComponentType<any>'.
|
|
44589
|
+
return widget?.getOneCorrectAnswerFromRubric?.(rubric);
|
|
44590
|
+
};
|
|
44591
|
+
|
|
44592
|
+
/* Verify if the widget key exists in the content string of the Perseus Item */
|
|
44593
|
+
const isWidgetKeyInContent = (perseusItem, widgetKey) => {
|
|
44594
|
+
return perseusItem.question.content.indexOf(widgetKey) !== -1;
|
|
44595
|
+
};
|
|
44596
|
+
|
|
44597
|
+
/* Return an array of all the widget keys that exist in the content string of a Perseus Item */
|
|
44598
|
+
const getValidWidgetKeys = perseusItem => {
|
|
44599
|
+
const {
|
|
44600
|
+
widgets
|
|
44601
|
+
} = perseusItem.question;
|
|
44602
|
+
return wonderStuffCore.keys(widgets).filter(key => isWidgetKeyInContent(perseusItem, key));
|
|
44603
|
+
};
|
|
44604
|
+
|
|
44300
44605
|
/**
|
|
44301
44606
|
* Helper to parse PerseusItem JSON
|
|
44302
44607
|
* Why not just use JSON.parse? We want:
|
|
@@ -44417,6 +44722,153 @@ function getWidgetTypeFromWidgetId(widgetId) {
|
|
|
44417
44722
|
return widgetType;
|
|
44418
44723
|
}
|
|
44419
44724
|
|
|
44725
|
+
let WidgetType = /*#__PURE__*/function (WidgetType) {
|
|
44726
|
+
WidgetType["Categorizer"] = "categorizer";
|
|
44727
|
+
WidgetType["CSProgram"] = "cs-program";
|
|
44728
|
+
WidgetType["Definition"] = "definition";
|
|
44729
|
+
WidgetType["Dropdown"] = "dropdown";
|
|
44730
|
+
WidgetType["Explanation"] = "explanation";
|
|
44731
|
+
WidgetType["Expression"] = "expression";
|
|
44732
|
+
WidgetType["GradedGroup"] = "graded-group";
|
|
44733
|
+
WidgetType["GradedGroupAnswerBar"] = "graded-group-answer-bar";
|
|
44734
|
+
WidgetType["GradedGroupSet"] = "graded-group-set";
|
|
44735
|
+
WidgetType["Grapher"] = "grapher";
|
|
44736
|
+
WidgetType["Group"] = "group";
|
|
44737
|
+
WidgetType["Iframe"] = "iframe";
|
|
44738
|
+
WidgetType["Image"] = "image";
|
|
44739
|
+
WidgetType["InputNumber"] = "input-number";
|
|
44740
|
+
WidgetType["Interaction"] = "interaction";
|
|
44741
|
+
WidgetType["InteractiveGraph"] = "interactive-graph";
|
|
44742
|
+
WidgetType["InteractiveNumberLine"] = "interactive-number-line";
|
|
44743
|
+
WidgetType["LabelImage"] = "label-image";
|
|
44744
|
+
WidgetType["Matcher"] = "matcher";
|
|
44745
|
+
WidgetType["Matrix"] = "matrix";
|
|
44746
|
+
WidgetType["Measurer"] = "measurer";
|
|
44747
|
+
WidgetType["Molecule"] = "molecule";
|
|
44748
|
+
WidgetType["NumberLine"] = "number-line";
|
|
44749
|
+
WidgetType["NumericInput"] = "numeric-input";
|
|
44750
|
+
WidgetType["Orderer"] = "orderer";
|
|
44751
|
+
WidgetType["Passage"] = "passage";
|
|
44752
|
+
WidgetType["PassageRef"] = "passage-ref";
|
|
44753
|
+
WidgetType["PassageRefTarget"] = "passage-ref-target";
|
|
44754
|
+
WidgetType["Plotter"] = "plotter";
|
|
44755
|
+
WidgetType["Protractor"] = "protractor";
|
|
44756
|
+
WidgetType["PythonProgram"] = "python-program";
|
|
44757
|
+
WidgetType["Radio"] = "radio";
|
|
44758
|
+
WidgetType["Sorter"] = "sorter";
|
|
44759
|
+
WidgetType["Table"] = "table";
|
|
44760
|
+
WidgetType["Unit"] = "unit";
|
|
44761
|
+
WidgetType["Video"] = "video";
|
|
44762
|
+
WidgetType["VideoTranscriptLink"] = "video-transcript-link";
|
|
44763
|
+
return WidgetType;
|
|
44764
|
+
}({});
|
|
44765
|
+
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
44766
|
+
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
44767
|
+
|
|
44768
|
+
/**
|
|
44769
|
+
* Regex for widget placeholders in a string.
|
|
44770
|
+
*
|
|
44771
|
+
* First capture group is the widget ID (ex. 'radio 1')
|
|
44772
|
+
* Second capture group is the widget type (ex. "radio)
|
|
44773
|
+
* exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
|
|
44774
|
+
*/
|
|
44775
|
+
function getWidgetRegex() {
|
|
44776
|
+
return /\[\[☃ (([a-z-]+) \d+)\]\]/g;
|
|
44777
|
+
}
|
|
44778
|
+
|
|
44779
|
+
/**
|
|
44780
|
+
* Add a widget placeholder using the provided widget type and instance number.
|
|
44781
|
+
* ex. addWidget(WidgetType.Radio, 1) => "[[☃ radio 1]]"
|
|
44782
|
+
*
|
|
44783
|
+
* @param {WidgetType} widgetType
|
|
44784
|
+
* @param {number} instance
|
|
44785
|
+
* @returns {string}
|
|
44786
|
+
*/
|
|
44787
|
+
function addWidget(widgetType, instance) {
|
|
44788
|
+
return `[[☃ ${widgetType} ${String(instance)}]]`;
|
|
44789
|
+
}
|
|
44790
|
+
|
|
44791
|
+
/**
|
|
44792
|
+
* Extract all widget IDs, which includes the widget type and instance number.
|
|
44793
|
+
* example output: ['radio 1', 'categorizer 1', 'categorizor 2']
|
|
44794
|
+
*
|
|
44795
|
+
* Content should contain Perseus widget placeholders,
|
|
44796
|
+
* which look like: '[[☃ radio 1]]'.
|
|
44797
|
+
*
|
|
44798
|
+
* @param {string} content
|
|
44799
|
+
* @returns {Array<WidgetId>} widgets
|
|
44800
|
+
*/
|
|
44801
|
+
function getAllWidgetIds(content) {
|
|
44802
|
+
const widgets = [];
|
|
44803
|
+
const localWidgetRegex = getWidgetRegex();
|
|
44804
|
+
let match = localWidgetRegex.exec(content);
|
|
44805
|
+
while (match !== null) {
|
|
44806
|
+
widgets.push(match[1]);
|
|
44807
|
+
match = localWidgetRegex.exec(content);
|
|
44808
|
+
}
|
|
44809
|
+
return widgets;
|
|
44810
|
+
}
|
|
44811
|
+
|
|
44812
|
+
/**
|
|
44813
|
+
* Check if a specific widget is an actual widget type using its name.
|
|
44814
|
+
* The widget name should include a widget type, without an instance number,
|
|
44815
|
+
* as a string. ex. 'radio'
|
|
44816
|
+
*
|
|
44817
|
+
* @param {string} widgetName
|
|
44818
|
+
* @returns {boolean}
|
|
44819
|
+
*/
|
|
44820
|
+
function isWidgetType(widgetName) {
|
|
44821
|
+
return Object.values(WidgetType).includes(widgetName);
|
|
44822
|
+
}
|
|
44823
|
+
|
|
44824
|
+
/**
|
|
44825
|
+
* Extract all widget types from a Perseus JSON content string.
|
|
44826
|
+
* This does not include the instance number and prevents duplicates.
|
|
44827
|
+
* example output: ['radio', 'categorizer']
|
|
44828
|
+
*
|
|
44829
|
+
* @param {string} content
|
|
44830
|
+
* @returns {Array<WidgetId>}
|
|
44831
|
+
*/
|
|
44832
|
+
function getAllWidgetTypes(content) {
|
|
44833
|
+
const widgetTypes = [];
|
|
44834
|
+
const localWidgetRegex = getWidgetRegex();
|
|
44835
|
+
let match = localWidgetRegex.exec(content);
|
|
44836
|
+
while (match !== null) {
|
|
44837
|
+
const matchType = match[2];
|
|
44838
|
+
if (!widgetTypes.includes(matchType) && isWidgetType(matchType)) {
|
|
44839
|
+
widgetTypes.push(matchType);
|
|
44840
|
+
}
|
|
44841
|
+
match = localWidgetRegex.exec(content);
|
|
44842
|
+
}
|
|
44843
|
+
return widgetTypes;
|
|
44844
|
+
}
|
|
44845
|
+
|
|
44846
|
+
/**
|
|
44847
|
+
* Check if a specific widget is a question widget type using its ID.
|
|
44848
|
+
* The widget ID includes the widget type and the instance number.
|
|
44849
|
+
*
|
|
44850
|
+
* @param {WidgetId} widgetId
|
|
44851
|
+
* @returns {boolean}
|
|
44852
|
+
*/
|
|
44853
|
+
function isQuestionWidgetType(widgetId) {
|
|
44854
|
+
return QUESTION_WIDGETS.includes(widgetId.split(" ")[0]);
|
|
44855
|
+
}
|
|
44856
|
+
|
|
44857
|
+
/**
|
|
44858
|
+
* Extract the widget IDs of all widgets considered question widgets. Widget IDs
|
|
44859
|
+
* include widget type and instance number.
|
|
44860
|
+
* example output: ['radio 1', 'categorizer 1']
|
|
44861
|
+
*
|
|
44862
|
+
* Content should contain Perseus widget placeholders,
|
|
44863
|
+
* which look like: '[[☃ radio 1]]'.
|
|
44864
|
+
*
|
|
44865
|
+
* @param {string} content
|
|
44866
|
+
* @returns Array<WidgetId>
|
|
44867
|
+
*/
|
|
44868
|
+
function getQuestionWidgetIds(content) {
|
|
44869
|
+
return getAllWidgetIds(content).filter(isQuestionWidgetType);
|
|
44870
|
+
}
|
|
44871
|
+
|
|
44420
44872
|
const EditorJsonify = {
|
|
44421
44873
|
serialize: function () {
|
|
44422
44874
|
// Omit props that get passed to all widgets
|
|
@@ -44458,6 +44910,7 @@ exports.PerseusError = PerseusError;
|
|
|
44458
44910
|
exports.PerseusExpressionAnswerFormConsidered = PerseusExpressionAnswerFormConsidered;
|
|
44459
44911
|
exports.PerseusMarkdown = PerseusMarkdown;
|
|
44460
44912
|
exports.PlotterWidget = Plotter$1;
|
|
44913
|
+
exports.QUESTION_WIDGETS = QUESTION_WIDGETS;
|
|
44461
44914
|
exports.Radio = Radio;
|
|
44462
44915
|
exports.Renderer = Renderer;
|
|
44463
44916
|
exports.ServerItemRenderer = ref;
|
|
@@ -44466,7 +44919,9 @@ exports.TableWidget = Table$1;
|
|
|
44466
44919
|
exports.Util = Util;
|
|
44467
44920
|
exports.WIDGET_PROP_DENYLIST = denylist;
|
|
44468
44921
|
exports.WidgetJsonifyDeprecated = WidgetJsonifyDeprecated;
|
|
44922
|
+
exports.WidgetType = WidgetType;
|
|
44469
44923
|
exports.Widgets = widgets$1;
|
|
44924
|
+
exports.addWidget = addWidget;
|
|
44470
44925
|
exports.apiVersion = apiVersion;
|
|
44471
44926
|
exports.buildEmptyItemTreeForShape = buildEmptyItemTreeForShape;
|
|
44472
44927
|
exports.buildMapper = buildMapper;
|
|
@@ -44476,8 +44931,15 @@ exports.convertWidgetNameToEnum = convertWidgetNameToEnum;
|
|
|
44476
44931
|
exports.displaySigFigs = displaySigFigs;
|
|
44477
44932
|
exports.generateTestPerseusItem = generateTestPerseusItem;
|
|
44478
44933
|
exports.genericPerseusItemData = genericPerseusItemData;
|
|
44934
|
+
exports.getAllWidgetIds = getAllWidgetIds;
|
|
44935
|
+
exports.getAllWidgetTypes = getAllWidgetTypes;
|
|
44936
|
+
exports.getAnswerFromUserInput = getAnswerFromUserInput;
|
|
44479
44937
|
exports.getAnswersFromWidgets = getAnswersFromWidgets;
|
|
44938
|
+
exports.getCorrectAnswerForWidgetKey = getCorrectAnswerForWidgetKey;
|
|
44939
|
+
exports.getQuestionWidgetIds = getQuestionWidgetIds;
|
|
44940
|
+
exports.getValidWidgetKeys = getValidWidgetKeys;
|
|
44480
44941
|
exports.getWidgetTypeFromWidgetId = getWidgetTypeFromWidgetId;
|
|
44942
|
+
exports.getWidgetTypeFromWidgetKey = getWidgetTypeFromWidgetKey;
|
|
44481
44943
|
exports.globalConstants = globalConstants;
|
|
44482
44944
|
exports.globalStyles = globalStyles;
|
|
44483
44945
|
exports.icons = iconPaths;
|
|
@@ -44485,6 +44947,8 @@ exports.init = init;
|
|
|
44485
44947
|
exports.injectWidgets = injectWidgets;
|
|
44486
44948
|
exports.interactiveSizes = interactiveSizes$1;
|
|
44487
44949
|
exports.isItemRenderableByVersion = isItemRenderableByVersion;
|
|
44950
|
+
exports.isWidgetKeyInContent = isWidgetKeyInContent;
|
|
44951
|
+
exports.isWrongAnswerSupported = isWrongAnswerSupported;
|
|
44488
44952
|
exports.itemDataVersion = itemDataVersion;
|
|
44489
44953
|
exports.itemToTree = itemToTree;
|
|
44490
44954
|
exports.itemVersion = ItemVersion;
|
|
@@ -44495,6 +44959,7 @@ exports.plotterPlotTypes = plotterPlotTypes;
|
|
|
44495
44959
|
exports.preprocessTex = preprocessTex;
|
|
44496
44960
|
exports.registerAllWidgetsForTesting = registerAllWidgetsForTesting;
|
|
44497
44961
|
exports.shapes = shapes;
|
|
44962
|
+
exports.shouldHaveIndividualAnswer = shouldHaveIndividualAnswer;
|
|
44498
44963
|
exports.traverse = traverse;
|
|
44499
44964
|
exports.violatingWidgets = violatingWidgets;
|
|
44500
44965
|
exports.widgets = allWidgets;
|