@khanacademy/perseus 53.0.0 → 54.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/highlighting/types.d.ts +2 -1
- package/dist/components.d.ts +0 -1
- package/dist/es/index.js +1101 -2034
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +101 -14
- package/dist/es/strings.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1180 -2058
- package/dist/index.js.map +1 -1
- package/dist/interactive2/movable.d.ts +2 -1
- package/dist/server-item-renderer.d.ts +0 -3
- package/dist/strings.d.ts +93 -6
- package/dist/strings.js +184 -58
- package/dist/strings.js.map +1 -1
- package/dist/types.d.ts +3 -24
- package/dist/util/graphie.d.ts +1 -1
- package/dist/widget-ai-utils/radio/radio-ai-utils.d.ts +2 -1
- package/dist/widget-type-utils.d.ts +1 -0
- package/dist/widgets/categorizer/categorizer.d.ts +1 -2
- package/dist/widgets/cs-program/cs-program.d.ts +0 -2
- package/dist/widgets/dropdown/dropdown.d.ts +0 -2
- package/dist/widgets/expression/expression.d.ts +3 -10
- package/dist/widgets/iframe/iframe.d.ts +1 -1
- package/dist/widgets/interactive-graph.d.ts +2 -86
- package/dist/widgets/interactive-graphs/backgrounds/axis-labels.d.ts +4 -1
- package/dist/widgets/interactive-graphs/graphs/angle.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/circle.d.ts +6 -2
- package/dist/widgets/interactive-graphs/graphs/components/hairlines.d.ts +7 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point-view.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/components/svg-line.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/linear-system.d.ts +2 -2
- package/dist/widgets/interactive-graphs/graphs/linear.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/point.d.ts +3 -2
- package/dist/widgets/interactive-graphs/graphs/polygon.d.ts +3 -2
- package/dist/widgets/interactive-graphs/graphs/ray.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/screenreader-text.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/segment.d.ts +2 -2
- package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/utils.d.ts +16 -4
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +1 -2
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +0 -2
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +3 -3
- package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +0 -2
- package/dist/widgets/label-image/label-image.d.ts +1 -3
- package/dist/widgets/number-line/number-line.d.ts +0 -2
- package/dist/widgets/numeric-input/numeric-input.d.ts +0 -2
- package/dist/widgets/orderer/orderer.d.ts +0 -2
- package/dist/widgets/sorter/sorter.d.ts +1 -2
- package/dist/widgets.d.ts +1 -2
- package/package.json +46 -46
- package/dist/components/graph.d.ts +0 -61
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +0 -4
package/dist/es/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as KAS from '@khanacademy/kas';
|
|
2
2
|
import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
|
|
3
|
-
import { PerseusError, Errors, expressionLogic,
|
|
3
|
+
import { PerseusError, Errors, expressionLogic, getDecimalSeparator, approximateDeepEqual, pluck, CoreWidgetRegistry, getUpgradedWidgetOptions, mapObject, passageRefLogic, radioLogic, GrapherUtil as GrapherUtil$1, lockedFigureColors, lockedFigureFillStyles, approximateEqual, getMatrixSize, measurerLogic, addLibraryVersionToPerseusDebug, upgradeWidgetInfoToLatestVersion, getWidgetIdsFromContentByType } from '@khanacademy/perseus-core';
|
|
4
4
|
export { isFailure, isSuccess, parseAndMigratePerseusArticle, parseAndMigratePerseusItem, parsePerseusItem } from '@khanacademy/perseus-core';
|
|
5
5
|
import * as PerseusLinter from '@khanacademy/perseus-linter';
|
|
6
6
|
import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
|
|
@@ -33,7 +33,6 @@ import Banner from '@khanacademy/wonder-blocks-banner';
|
|
|
33
33
|
import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-blocks-dropdown';
|
|
34
34
|
import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
|
|
35
35
|
import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
|
|
36
|
-
import debounce$2 from 'lodash.debounce';
|
|
37
36
|
import { vec, Coordinates, Ellipse, Plot as Plot$2, Line as Line$4, Point as Point$2, Polygon, Text as Text$1, useTransformContext, Polyline, Mafs } from 'mafs';
|
|
38
37
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
39
38
|
import questionIcon from '@phosphor-icons/core/regular/question.svg';
|
|
@@ -240,6 +239,10 @@ const mockStrings = {
|
|
|
240
239
|
simulationLocaleWarning: "Sorry, this simulation isn't available in your language.",
|
|
241
240
|
selectAnAnswer: "Select an answer",
|
|
242
241
|
// The following strings are used for interactive graph SR descriptions.
|
|
242
|
+
srGraphInstructions: "Use the Tab key to move through the interactive elements in the graph. When an interactive element has focus, use the Arrow keys to move it.",
|
|
243
|
+
srUnlimitedGraphInstructions: "Press Shift + Enter to interact with the graph. Use the Tab key to move through the interactive elements in the graph and access the graph Action Bar. When an interactive element has focus, use the Arrow keys to move it or use the Delete key to remove it from the graph. Use the buttons in the Action Bar to add or adjust elements within the graph.",
|
|
244
|
+
xAxis: "X-axis",
|
|
245
|
+
yAxis: "Y-axis",
|
|
243
246
|
graphKeyboardPrompt: "Press Shift + Enter to interact with the graph",
|
|
244
247
|
addPoint: "Add Point",
|
|
245
248
|
removePoint: "Remove Point",
|
|
@@ -249,7 +252,7 @@ const mockStrings = {
|
|
|
249
252
|
num,
|
|
250
253
|
x,
|
|
251
254
|
y
|
|
252
|
-
}) => `Point ${num} at ${x} comma ${y}
|
|
255
|
+
}) => `Point ${num} at ${x} comma ${y}.`,
|
|
253
256
|
srInteractiveElements: ({
|
|
254
257
|
elements
|
|
255
258
|
}) => `Interactive elements: ${elements}`,
|
|
@@ -304,17 +307,23 @@ const mockStrings = {
|
|
|
304
307
|
point2X,
|
|
305
308
|
point2Y
|
|
306
309
|
}) => `Line from ${point1X} comma ${point1Y} to ${point2X} comma ${point2Y}.`,
|
|
307
|
-
|
|
308
|
-
point,
|
|
309
|
-
side,
|
|
310
|
+
srAngleStartingSide: ({
|
|
310
311
|
x,
|
|
311
312
|
y
|
|
312
|
-
}) => `Point
|
|
313
|
-
|
|
313
|
+
}) => `Point 3, starting side at ${x} comma ${y}.`,
|
|
314
|
+
srAngleEndingSide: ({
|
|
315
|
+
x,
|
|
316
|
+
y
|
|
317
|
+
}) => `Point 2, ending side at ${x} comma ${y}.`,
|
|
318
|
+
srAngleVertex: ({
|
|
319
|
+
x,
|
|
320
|
+
y
|
|
321
|
+
}) => `Point 1, vertex at ${x} comma ${y}.`,
|
|
322
|
+
srAngleVertexWithAngleMeasure: ({
|
|
314
323
|
x,
|
|
315
324
|
y,
|
|
316
325
|
angleMeasure
|
|
317
|
-
}) => `Point
|
|
326
|
+
}) => `Point 1, vertex at ${x} comma ${y}. Angle ${angleMeasure} degrees.`,
|
|
318
327
|
srAngleGraphAriaLabel: "An angle on a coordinate plane.",
|
|
319
328
|
srAngleGraphAriaDescription: ({
|
|
320
329
|
angleMeasure,
|
|
@@ -325,6 +334,14 @@ const mockStrings = {
|
|
|
325
334
|
endingSideX,
|
|
326
335
|
endingSideY
|
|
327
336
|
}) => `The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the starting side at ${startingSideX} comma ${startingSideY} and a point on the ending side at ${endingSideX} comma ${endingSideY}.`,
|
|
337
|
+
srAngleInteractiveElements: ({
|
|
338
|
+
vertexX,
|
|
339
|
+
vertexY,
|
|
340
|
+
startingSideX,
|
|
341
|
+
startingSideY,
|
|
342
|
+
endingSideX,
|
|
343
|
+
endingSideY
|
|
344
|
+
}) => `An angle formed by 3 points. The vertex is at ${vertexX} comma ${vertexY}. The starting side point is at ${startingSideX} comma ${startingSideY}. The ending side point is at ${endingSideX} comma ${endingSideY}.`,
|
|
328
345
|
srSingleSegmentGraphAriaLabel: "A line segment on a coordinate plane.",
|
|
329
346
|
srMultipleSegmentGraphAriaLabel: ({
|
|
330
347
|
countOfSegments
|
|
@@ -437,7 +454,53 @@ const mockStrings = {
|
|
|
437
454
|
point2Y,
|
|
438
455
|
point3X,
|
|
439
456
|
point3Y
|
|
440
|
-
}) => `Parabola with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and ${point3X} comma ${point3Y}
|
|
457
|
+
}) => `Parabola with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and ${point3X} comma ${point3Y}.`,
|
|
458
|
+
srPolygonGraph: "A polygon.",
|
|
459
|
+
srPolygonGraphCoordinatePlane: "A polygon on a coordinate plane.",
|
|
460
|
+
srPolygonGraphPointsNum: ({
|
|
461
|
+
num
|
|
462
|
+
}) => `The polygon has ${num} points.`,
|
|
463
|
+
srPolygonGraphPointsOne: "The polygon has 1 point.",
|
|
464
|
+
srPolygonElementsNum: ({
|
|
465
|
+
num
|
|
466
|
+
}) => `A polygon with ${num} points.`,
|
|
467
|
+
srPolygonElementsOne: "A polygon with 1 point.",
|
|
468
|
+
srPolygonPointAngleApprox: ({
|
|
469
|
+
angle
|
|
470
|
+
}) => `Angle approximately equal to ${angle} degrees.`,
|
|
471
|
+
srPolygonPointAngle: ({
|
|
472
|
+
angle
|
|
473
|
+
}) => `Angle equal to ${angle} degrees.`,
|
|
474
|
+
srPolygonSideLength: ({
|
|
475
|
+
pointNum,
|
|
476
|
+
length
|
|
477
|
+
}) => `A line segment, length equal to ${length} units, connects to point ${pointNum}.`,
|
|
478
|
+
srPolygonSideLengthApprox: ({
|
|
479
|
+
pointNum,
|
|
480
|
+
length
|
|
481
|
+
}) => `A line segment, length approximately equal to ${length} units, connects to point ${pointNum}.`,
|
|
482
|
+
srUnlimitedPolygonEmpty: "An empty coordinate plane.",
|
|
483
|
+
srSinusoidGraph: "A sinusoid function on a coordinate plane.",
|
|
484
|
+
srSinusoidRootPoint: ({
|
|
485
|
+
x,
|
|
486
|
+
y
|
|
487
|
+
}) => `Midline intersection at ${x} comma ${y}.`,
|
|
488
|
+
srSinusoidPeakPoint: ({
|
|
489
|
+
x,
|
|
490
|
+
y
|
|
491
|
+
}) => `Extremum point at ${x} comma ${y}.`,
|
|
492
|
+
srSinusoidDescription: ({
|
|
493
|
+
minValue,
|
|
494
|
+
maxValue,
|
|
495
|
+
cycleStart,
|
|
496
|
+
cycleEnd
|
|
497
|
+
}) => `The graph shows a wave with a minimum value of ${minValue} and a maximum value of ${maxValue}. The wave completes a full cycle from ${cycleStart} to ${cycleEnd}.`,
|
|
498
|
+
srSinusoidInteractiveElements: ({
|
|
499
|
+
point1X,
|
|
500
|
+
point1Y,
|
|
501
|
+
point2X,
|
|
502
|
+
point2Y
|
|
503
|
+
}) => `Sinusoid graph with midline intersection point at ${point1X} comma ${point1Y} and extremum point at ${point2X} comma ${point2Y}.`
|
|
441
504
|
// The above strings are used for interactive graph SR descriptions.
|
|
442
505
|
};
|
|
443
506
|
|
|
@@ -1369,6 +1432,7 @@ const ApiOptions = {
|
|
|
1369
1432
|
},
|
|
1370
1433
|
baseElements: {
|
|
1371
1434
|
Link: props => {
|
|
1435
|
+
// eslint-disable-next-line jsx-a11y/anchor-has-content -- TODO(LEMS-2871): Address a11y error
|
|
1372
1436
|
return /*#__PURE__*/React.createElement("a", props);
|
|
1373
1437
|
}
|
|
1374
1438
|
},
|
|
@@ -1772,7 +1836,6 @@ var Expression$1 = {
|
|
|
1772
1836
|
propUpgrades: expressionLogic.widgetOptionsUpgrades,
|
|
1773
1837
|
// For use by the editor
|
|
1774
1838
|
isLintable: true,
|
|
1775
|
-
getPublicWidgetOptions: getExpressionPublicWidgetOptions,
|
|
1776
1839
|
// TODO(LEMS-2656): remove TS suppression
|
|
1777
1840
|
// @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusExpressionRubric'.
|
|
1778
1841
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
@@ -9838,7 +9901,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
9838
9901
|
|
|
9839
9902
|
/* eslint-disable @babel/no-invalid-this */
|
|
9840
9903
|
const {
|
|
9841
|
-
clockwise: clockwise$
|
|
9904
|
+
clockwise: clockwise$2,
|
|
9842
9905
|
reverseVector: reverseVector$1
|
|
9843
9906
|
} = geometry;
|
|
9844
9907
|
function scaledDistanceFromAngle(angle) {
|
|
@@ -11076,7 +11139,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
11076
11139
|
// Temporary objects
|
|
11077
11140
|
_.invoke(polygon.temp, "remove");
|
|
11078
11141
|
polygon.temp = [];
|
|
11079
|
-
const isClockwise = clockwise$
|
|
11142
|
+
const isClockwise = clockwise$2(polygon.coords);
|
|
11080
11143
|
|
|
11081
11144
|
// Update angle labels
|
|
11082
11145
|
if (polygon.angleLabels.length || polygon.showRightAngleMarkers.length) {
|
|
@@ -14467,7 +14530,7 @@ function sum(array) {
|
|
|
14467
14530
|
return memo + arg;
|
|
14468
14531
|
}, 0);
|
|
14469
14532
|
}
|
|
14470
|
-
function clockwise$
|
|
14533
|
+
function clockwise$1(points) {
|
|
14471
14534
|
const segments = _.zip(points, points.slice(1).concat(points.slice(0, 1)));
|
|
14472
14535
|
const areas = _.map(segments, function (segment) {
|
|
14473
14536
|
const p1 = segment[0];
|
|
@@ -14533,7 +14596,7 @@ const draw$1 = {
|
|
|
14533
14596
|
const graphie = this.graphie;
|
|
14534
14597
|
const self = this;
|
|
14535
14598
|
const coords = _.invoke(state.points, "coord");
|
|
14536
|
-
const isClockwise = clockwise$
|
|
14599
|
+
const isClockwise = clockwise$1(coords);
|
|
14537
14600
|
const n = coords.length;
|
|
14538
14601
|
|
|
14539
14602
|
// graphie.labelAngle and similar methods attempt to re-use the label
|
|
@@ -15674,12 +15737,17 @@ class ImageLoader extends React.Component {
|
|
|
15674
15737
|
};
|
|
15675
15738
|
}
|
|
15676
15739
|
const staticUrl = getDependencies().staticUrl;
|
|
15677
|
-
return
|
|
15678
|
-
|
|
15679
|
-
|
|
15680
|
-
|
|
15681
|
-
|
|
15682
|
-
|
|
15740
|
+
return (
|
|
15741
|
+
/*#__PURE__*/
|
|
15742
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
15743
|
+
React.createElement("img", _extends({
|
|
15744
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- TODO(LEMS-2871): Address a11y error
|
|
15745
|
+
tabIndex: 0,
|
|
15746
|
+
src: staticUrl(src),
|
|
15747
|
+
onKeyUp: onKeyUp,
|
|
15748
|
+
onKeyDown: onKeyDown
|
|
15749
|
+
}, imgProps))
|
|
15750
|
+
);
|
|
15683
15751
|
};
|
|
15684
15752
|
this.state = {
|
|
15685
15753
|
status: props.src ? Status.LOADING : Status.PENDING
|
|
@@ -16270,9 +16338,29 @@ class Zoomable extends React.Component {
|
|
|
16270
16338
|
if (!this._isMounted) {
|
|
16271
16339
|
return;
|
|
16272
16340
|
}
|
|
16341
|
+
|
|
16342
|
+
// When a zoomable item is contained within a collapsed parent (such as an Explanation widget),
|
|
16343
|
+
// the parent node has zero width, which causes the scale to be miscalculated.
|
|
16344
|
+
// Therefore, we check to see if the node has a valid width, and if it doesn't,
|
|
16345
|
+
// then we traverse up the DOM tree and look for the closest "perseus-renderer"
|
|
16346
|
+
// (which should have a width).
|
|
16347
|
+
// To be cautious, we also account for the possibility that we can't find any parents with a valid width
|
|
16348
|
+
// (not that they don't exist, just that we can't find them).
|
|
16349
|
+
let parentNode = this._node;
|
|
16350
|
+
let currentNode = parentNode;
|
|
16351
|
+
while (currentNode && currentNode.offsetWidth === 0) {
|
|
16352
|
+
var _currentNode$parentEl, _currentNode$parentEl2;
|
|
16353
|
+
// When traversing up the DOM tree, we need to start at the "parentElement"
|
|
16354
|
+
// because "closest()" can match the current element,
|
|
16355
|
+
// and if it does so, then we are stuck in an infinite loop.
|
|
16356
|
+
currentNode = (_currentNode$parentEl = (_currentNode$parentEl2 = currentNode.parentElement) == null ? void 0 : _currentNode$parentEl2.closest(".perseus-renderer")) != null ? _currentNode$parentEl : null;
|
|
16357
|
+
}
|
|
16358
|
+
if (currentNode) {
|
|
16359
|
+
parentNode = currentNode;
|
|
16360
|
+
}
|
|
16273
16361
|
const parentBounds = {
|
|
16274
|
-
width:
|
|
16275
|
-
height:
|
|
16362
|
+
width: parentNode.offsetWidth,
|
|
16363
|
+
height: parentNode.offsetHeight
|
|
16276
16364
|
};
|
|
16277
16365
|
const childBounds = this.props.computeChildBounds(this._node, parentBounds);
|
|
16278
16366
|
|
|
@@ -16344,16 +16432,20 @@ class Zoomable extends React.Component {
|
|
|
16344
16432
|
opacity: opacity,
|
|
16345
16433
|
WebkitTapHighlightColor: "transparent"
|
|
16346
16434
|
}, transitionStyle);
|
|
16347
|
-
return
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16435
|
+
return (
|
|
16436
|
+
/*#__PURE__*/
|
|
16437
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events -- TODO(LEMS-2871): Address a11y error
|
|
16438
|
+
React.createElement("span", {
|
|
16439
|
+
onClick: this.handleClick,
|
|
16440
|
+
onClickCapture: this.handleClickIfZoomed,
|
|
16441
|
+
onTouchCancelCapture: this.stopPropagationIfZoomed,
|
|
16442
|
+
onTouchEndCapture: this.stopPropagationIfZoomed,
|
|
16443
|
+
onTouchStartCapture: this.stopPropagationIfZoomed
|
|
16444
|
+
// @ts-expect-error - TS2322 - Type '{ readonly transitionProperty: string; readonly transitionDuration: string; readonly transitionTimingFunction: string; readonly display: "block"; readonly width: "100%"; readonly height: number | ... 1 more ... | undefined; readonly transform: string; readonly transformOrigin: "0 0"; readonly opacity: 0 | 1; readonl...' is not assignable to type 'CSSProperties | undefined'.
|
|
16445
|
+
,
|
|
16446
|
+
style: style
|
|
16447
|
+
}, this.props.children)
|
|
16448
|
+
);
|
|
16357
16449
|
}
|
|
16358
16450
|
}
|
|
16359
16451
|
Zoomable.defaultProps = {
|
|
@@ -17574,10 +17666,6 @@ const getWidgetExport = name => {
|
|
|
17574
17666
|
var _widgets$name3;
|
|
17575
17667
|
return (_widgets$name3 = widgets[name]) != null ? _widgets$name3 : null;
|
|
17576
17668
|
};
|
|
17577
|
-
const getPublicWidgetOptionsFunction = name => {
|
|
17578
|
-
var _widgets$name$getPubl, _widgets$name4;
|
|
17579
|
-
return (_widgets$name$getPubl = (_widgets$name4 = widgets[name]) == null ? void 0 : _widgets$name4.getPublicWidgetOptions) != null ? _widgets$name$getPubl : i => i;
|
|
17580
|
-
};
|
|
17581
17669
|
const getEditor = name => {
|
|
17582
17670
|
return _.has(editors, name) ? editors[name] : null;
|
|
17583
17671
|
};
|
|
@@ -17714,7 +17802,6 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
17714
17802
|
replaceDeprecatedEditors: replaceDeprecatedEditors,
|
|
17715
17803
|
getWidget: getWidget,
|
|
17716
17804
|
getWidgetExport: getWidgetExport,
|
|
17717
|
-
getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
|
|
17718
17805
|
getEditor: getEditor,
|
|
17719
17806
|
getTransform: getTransform,
|
|
17720
17807
|
getVersion: getVersion,
|
|
@@ -17790,6 +17877,7 @@ class WidgetContainer extends React.Component {
|
|
|
17790
17877
|
"perseus-widget__definition": this.props.type === "definition"
|
|
17791
17878
|
});
|
|
17792
17879
|
const type = this.props.type;
|
|
17880
|
+
const userAgent = navigator.userAgent;
|
|
17793
17881
|
const WidgetType = getWidget(type);
|
|
17794
17882
|
if (WidgetType == null) {
|
|
17795
17883
|
// This is for the good of all people!!
|
|
@@ -17800,6 +17888,12 @@ class WidgetContainer extends React.Component {
|
|
|
17800
17888
|
className: className
|
|
17801
17889
|
});
|
|
17802
17890
|
}
|
|
17891
|
+
let subType = "null";
|
|
17892
|
+
if (type === "interactive-graph") {
|
|
17893
|
+
var _props$graph$type, _props$graph;
|
|
17894
|
+
const props = this.state.widgetProps;
|
|
17895
|
+
subType = (_props$graph$type = (_props$graph = props.graph) == null ? void 0 : _props$graph.type) != null ? _props$graph$type : "null";
|
|
17896
|
+
}
|
|
17803
17897
|
let alignment = this.state.widgetProps.alignment;
|
|
17804
17898
|
if (alignment === "default") {
|
|
17805
17899
|
alignment = CoreWidgetRegistry.getDefaultAlignment(type);
|
|
@@ -17853,12 +17947,26 @@ class WidgetContainer extends React.Component {
|
|
|
17853
17947
|
widget_type: type,
|
|
17854
17948
|
widget_id: this.props.id
|
|
17855
17949
|
},
|
|
17856
|
-
onError:
|
|
17950
|
+
onError: error => {
|
|
17951
|
+
// TODO(LEMS-2826): Remove analytics event in LEMS-2826 in favor of ti below.
|
|
17857
17952
|
analytics.onAnalyticsEvent({
|
|
17858
17953
|
type: "perseus:widget-rendering-error",
|
|
17859
17954
|
payload: {
|
|
17955
|
+
widgetSubType: subType,
|
|
17860
17956
|
widgetType: type,
|
|
17861
|
-
widgetId: this.props.id
|
|
17957
|
+
widgetId: this.props.id,
|
|
17958
|
+
message: error.message,
|
|
17959
|
+
userAgent: userAgent
|
|
17960
|
+
}
|
|
17961
|
+
});
|
|
17962
|
+
analytics.onAnalyticsEvent({
|
|
17963
|
+
type: "perseus:widget-rendering-error:ti",
|
|
17964
|
+
payload: {
|
|
17965
|
+
widgetSubType: subType,
|
|
17966
|
+
widgetType: type,
|
|
17967
|
+
widgetId: this.props.id,
|
|
17968
|
+
message: error.message,
|
|
17969
|
+
userAgent: userAgent
|
|
17862
17970
|
}
|
|
17863
17971
|
});
|
|
17864
17972
|
}
|
|
@@ -19292,7 +19400,7 @@ function _objectWithoutPropertiesLoose(r, e) {
|
|
|
19292
19400
|
if (null == r) return {};
|
|
19293
19401
|
var t = {};
|
|
19294
19402
|
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
19295
|
-
if (e.
|
|
19403
|
+
if (-1 !== e.indexOf(n)) continue;
|
|
19296
19404
|
t[n] = r[n];
|
|
19297
19405
|
}
|
|
19298
19406
|
return t;
|
|
@@ -20282,7 +20390,6 @@ var NumericInput$1 = {
|
|
|
20282
20390
|
widget: NumericInput,
|
|
20283
20391
|
transform: propsTransform,
|
|
20284
20392
|
isLintable: true,
|
|
20285
|
-
getPublicWidgetOptions: getNumericInputPublicWidgetOptions,
|
|
20286
20393
|
// TODO(LEMS-2656): remove TS suppression
|
|
20287
20394
|
// @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusNumericInputRubric'
|
|
20288
20395
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
@@ -21087,7 +21194,10 @@ class HighlightTooltip extends React.PureComponent {
|
|
|
21087
21194
|
// using div instead of TooltipContent because
|
|
21088
21195
|
// TooltipContent wouldn't let me overwrite
|
|
21089
21196
|
// user-select and onClick
|
|
21090
|
-
const content =
|
|
21197
|
+
const content =
|
|
21198
|
+
/*#__PURE__*/
|
|
21199
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events -- TODO(LEMS-2871): Address a11y error
|
|
21200
|
+
React.createElement("div", {
|
|
21091
21201
|
className: css(styles$x.tooltipLabel),
|
|
21092
21202
|
onClick: this.props.onClick
|
|
21093
21203
|
}, this.props.label);
|
|
@@ -21098,14 +21208,18 @@ class HighlightTooltip extends React.PureComponent {
|
|
|
21098
21208
|
top: `calc(${Math.round(focusRect.top)}px + 0.95em)`,
|
|
21099
21209
|
height: 0
|
|
21100
21210
|
};
|
|
21101
|
-
return
|
|
21102
|
-
|
|
21103
|
-
|
|
21104
|
-
|
|
21105
|
-
|
|
21106
|
-
|
|
21107
|
-
|
|
21108
|
-
|
|
21211
|
+
return (
|
|
21212
|
+
/*#__PURE__*/
|
|
21213
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
21214
|
+
React.createElement("div", {
|
|
21215
|
+
style: style,
|
|
21216
|
+
onMouseEnter: this.props.onMouseEnter,
|
|
21217
|
+
onMouseLeave: this.props.onMouseLeave
|
|
21218
|
+
}, /*#__PURE__*/React.createElement(Tooltip$1, {
|
|
21219
|
+
content: content,
|
|
21220
|
+
opened: true
|
|
21221
|
+
}, /*#__PURE__*/React.createElement("div", null)))
|
|
21222
|
+
);
|
|
21109
21223
|
}
|
|
21110
21224
|
}
|
|
21111
21225
|
const styles$x = StyleSheet.create({
|
|
@@ -23061,8 +23175,8 @@ const styles$s = StyleSheet.create({
|
|
|
23061
23175
|
crossOutLine: {
|
|
23062
23176
|
// Center the icon within the container.
|
|
23063
23177
|
position: "absolute",
|
|
23064
|
-
top: `
|
|
23065
|
-
left: `
|
|
23178
|
+
top: `2px`,
|
|
23179
|
+
left: `2px`
|
|
23066
23180
|
}
|
|
23067
23181
|
});
|
|
23068
23182
|
|
|
@@ -23089,43 +23203,6 @@ function ChoiceInner(props) {
|
|
|
23089
23203
|
}
|
|
23090
23204
|
return /*#__PURE__*/React.createElement(InlineIcon, iconMinus);
|
|
23091
23205
|
}
|
|
23092
|
-
|
|
23093
|
-
// Handle dynamic styling of the multiple choice icon. Most
|
|
23094
|
-
// MC icon styles are constant, but we do allow the caller
|
|
23095
|
-
// to specify the selected color, and thus must control styles
|
|
23096
|
-
// related to the selected state dynamically.
|
|
23097
|
-
function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct) {
|
|
23098
|
-
let backgroundColor;
|
|
23099
|
-
let borderColor;
|
|
23100
|
-
let color$1;
|
|
23101
|
-
if (!showCorrectness && pressed) {
|
|
23102
|
-
borderColor = color.blue;
|
|
23103
|
-
color$1 = color.blue;
|
|
23104
|
-
backgroundColor = "transparent";
|
|
23105
|
-
} else if (checked) {
|
|
23106
|
-
const bg = showCorrectness && correct ? color.green : color.blue;
|
|
23107
|
-
color$1 = white;
|
|
23108
|
-
backgroundColor = bg;
|
|
23109
|
-
borderColor = bg;
|
|
23110
|
-
} else {
|
|
23111
|
-
borderColor = color.offBlack64;
|
|
23112
|
-
color$1 = color.offBlack64;
|
|
23113
|
-
}
|
|
23114
|
-
|
|
23115
|
-
// define shape
|
|
23116
|
-
let borderRadius;
|
|
23117
|
-
if (multipleSelect) {
|
|
23118
|
-
borderRadius = 3;
|
|
23119
|
-
} else {
|
|
23120
|
-
borderRadius = CHOICE_ICON_SIZE;
|
|
23121
|
-
}
|
|
23122
|
-
return {
|
|
23123
|
-
backgroundColor,
|
|
23124
|
-
borderColor,
|
|
23125
|
-
color: color$1,
|
|
23126
|
-
borderRadius
|
|
23127
|
-
};
|
|
23128
|
-
}
|
|
23129
23206
|
const ChoiceIcon = function ChoiceIcon(props) {
|
|
23130
23207
|
const {
|
|
23131
23208
|
checked,
|
|
@@ -23137,9 +23214,41 @@ const ChoiceIcon = function ChoiceIcon(props) {
|
|
|
23137
23214
|
multipleSelect,
|
|
23138
23215
|
pos,
|
|
23139
23216
|
previouslyAnswered,
|
|
23217
|
+
// Used in "review mode"/"show rationale" to show that option was previously chosen
|
|
23140
23218
|
pressed
|
|
23141
23219
|
} = props;
|
|
23142
|
-
|
|
23220
|
+
|
|
23221
|
+
// Accounts for incorrect choice still displaying as learner tries again
|
|
23222
|
+
const choiceIsChecked = checked || showCorrectness && !correct && previouslyAnswered;
|
|
23223
|
+
|
|
23224
|
+
// Core styling
|
|
23225
|
+
const choiceStyling = [styles$r.choiceBase, multipleSelect ? styles$r.multiSelectShape : styles$r.singleSelectShape, showCorrectness ? styles$r.choiceHasIcon : styles$r.choiceHasLetter, choiceIsChecked ? styles$r.choiceIsChecked : styles$r.choiceIsUnchecked];
|
|
23226
|
+
|
|
23227
|
+
// Color styling
|
|
23228
|
+
// Handle dynamic styling of the multiple choice icon. Most
|
|
23229
|
+
// MC icon styles are constant, but we do allow the caller
|
|
23230
|
+
// to specify the selected color, and thus must control styles
|
|
23231
|
+
// related to the selected state dynamically.
|
|
23232
|
+
let crossOutColor;
|
|
23233
|
+
if (showCorrectness && correct && checked) {
|
|
23234
|
+
choiceStyling.push(styles$r.choiceCorrect);
|
|
23235
|
+
crossOutColor = color.green;
|
|
23236
|
+
} else if (showCorrectness && !correct && (checked || previouslyAnswered)) {
|
|
23237
|
+
choiceStyling.push(styles$r.choiceIncorrect);
|
|
23238
|
+
crossOutColor = color.red;
|
|
23239
|
+
} else if (checked) {
|
|
23240
|
+
// Show filled neutral blue color (showCorrectness is false)
|
|
23241
|
+
choiceStyling.push(styles$r.choiceNeutral);
|
|
23242
|
+
crossOutColor = color.blue;
|
|
23243
|
+
} else if (pressed) {
|
|
23244
|
+
// Show outlined neutral blue color (showCorrectness is false)
|
|
23245
|
+
choiceStyling.push(styles$r.activeNeutral);
|
|
23246
|
+
crossOutColor = color.blue;
|
|
23247
|
+
} else {
|
|
23248
|
+
// choice is not checked
|
|
23249
|
+
choiceStyling.push(styles$r.uncheckedColors);
|
|
23250
|
+
crossOutColor = color.offBlack64;
|
|
23251
|
+
}
|
|
23143
23252
|
return /*#__PURE__*/React.createElement("div", {
|
|
23144
23253
|
className: css(styles$t.iconWrapper)
|
|
23145
23254
|
}, /*#__PURE__*/React.createElement(FocusRing, {
|
|
@@ -23147,9 +23256,8 @@ const ChoiceIcon = function ChoiceIcon(props) {
|
|
|
23147
23256
|
visible: focused || hovered,
|
|
23148
23257
|
multipleSelect: multipleSelect
|
|
23149
23258
|
}, /*#__PURE__*/React.createElement("div", {
|
|
23150
|
-
style: dynamicStyles,
|
|
23151
23259
|
"data-testid": "choice-icon__library-choice-icon",
|
|
23152
|
-
className: css(
|
|
23260
|
+
className: css(...choiceStyling)
|
|
23153
23261
|
// used in BaseRadio editMode to check
|
|
23154
23262
|
// if we actually clicked on the radio icon
|
|
23155
23263
|
,
|
|
@@ -23161,7 +23269,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
|
|
|
23161
23269
|
showCorrectness: showCorrectness,
|
|
23162
23270
|
correct: correct
|
|
23163
23271
|
})))), crossedOut && /*#__PURE__*/React.createElement(CrossOutLine, {
|
|
23164
|
-
color:
|
|
23272
|
+
color: crossOutColor
|
|
23165
23273
|
}));
|
|
23166
23274
|
};
|
|
23167
23275
|
const styles$r = StyleSheet.create({
|
|
@@ -23173,8 +23281,7 @@ const styles$r = StyleSheet.create({
|
|
|
23173
23281
|
alignItems: "center",
|
|
23174
23282
|
justifyContent: "center"
|
|
23175
23283
|
},
|
|
23176
|
-
|
|
23177
|
-
// Make the circle
|
|
23284
|
+
choiceBase: {
|
|
23178
23285
|
width: CHOICE_ICON_SIZE,
|
|
23179
23286
|
height: CHOICE_ICON_SIZE,
|
|
23180
23287
|
boxSizing: "border-box",
|
|
@@ -23186,30 +23293,48 @@ const styles$r = StyleSheet.create({
|
|
|
23186
23293
|
// "bold font family" so that characters which fall back to the default
|
|
23187
23294
|
// font get bolded too.
|
|
23188
23295
|
fontWeight: "bold",
|
|
23189
|
-
fontSize: 12,
|
|
23190
23296
|
// Center the icon wrapper.
|
|
23191
23297
|
display: "flex",
|
|
23192
23298
|
alignItems: "center",
|
|
23193
|
-
justifyContent: "center"
|
|
23194
|
-
|
|
23195
|
-
|
|
23196
|
-
|
|
23197
|
-
|
|
23198
|
-
|
|
23199
|
-
lineHeight: "1px"
|
|
23200
|
-
},
|
|
23201
|
-
circleCorrect: {
|
|
23299
|
+
justifyContent: "center"
|
|
23300
|
+
},
|
|
23301
|
+
choiceHasLetter: {
|
|
23302
|
+
fontSize: 12
|
|
23303
|
+
},
|
|
23304
|
+
choiceHasIcon: {
|
|
23202
23305
|
fontSize: CHOICE_ICON_SIZE
|
|
23203
23306
|
},
|
|
23204
|
-
|
|
23205
|
-
|
|
23206
|
-
borderColor: gray68,
|
|
23207
|
-
color: gray68
|
|
23307
|
+
choiceIsChecked: {
|
|
23308
|
+
color: color.white
|
|
23208
23309
|
},
|
|
23209
|
-
|
|
23310
|
+
choiceIsUnchecked: {
|
|
23311
|
+
color: color.offBlack64
|
|
23312
|
+
},
|
|
23313
|
+
choiceCorrect: {
|
|
23314
|
+
backgroundColor: color.green,
|
|
23315
|
+
borderColor: color.green
|
|
23316
|
+
},
|
|
23317
|
+
choiceIncorrect: {
|
|
23210
23318
|
backgroundColor: color.red,
|
|
23211
|
-
borderColor: color.red
|
|
23212
|
-
|
|
23319
|
+
borderColor: color.red
|
|
23320
|
+
},
|
|
23321
|
+
choiceNeutral: {
|
|
23322
|
+
backgroundColor: color.blue,
|
|
23323
|
+
borderColor: color.blue
|
|
23324
|
+
},
|
|
23325
|
+
activeNeutral: {
|
|
23326
|
+
color: color.blue,
|
|
23327
|
+
borderColor: color.blue,
|
|
23328
|
+
backgroundColor: "transparent"
|
|
23329
|
+
},
|
|
23330
|
+
multiSelectShape: {
|
|
23331
|
+
borderRadius: 3
|
|
23332
|
+
},
|
|
23333
|
+
singleSelectShape: {
|
|
23334
|
+
borderRadius: CHOICE_ICON_SIZE
|
|
23335
|
+
},
|
|
23336
|
+
uncheckedColors: {
|
|
23337
|
+
borderColor: color.offBlack64
|
|
23213
23338
|
}
|
|
23214
23339
|
});
|
|
23215
23340
|
|
|
@@ -23797,15 +23922,19 @@ const BaseRadio = function BaseRadio({
|
|
|
23797
23922
|
// content somehow? Would changing our choice of key
|
|
23798
23923
|
// somehow break something happening inside a choice's
|
|
23799
23924
|
// child Renderers, by changing when we mount/unmount?
|
|
23800
|
-
return
|
|
23801
|
-
|
|
23802
|
-
|
|
23803
|
-
|
|
23804
|
-
|
|
23805
|
-
|
|
23806
|
-
|
|
23807
|
-
|
|
23808
|
-
|
|
23925
|
+
return (
|
|
23926
|
+
/*#__PURE__*/
|
|
23927
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
23928
|
+
React.createElement("li", {
|
|
23929
|
+
key: i,
|
|
23930
|
+
ref: e => listElem = e,
|
|
23931
|
+
className: className,
|
|
23932
|
+
onClick: clickHandler,
|
|
23933
|
+
onTouchStart: labelWrap ? undefined : captureScratchpadTouchStart$1
|
|
23934
|
+
}, /*#__PURE__*/React.createElement(Element, _extends({}, elementProps, {
|
|
23935
|
+
ref: ref
|
|
23936
|
+
})))
|
|
23937
|
+
);
|
|
23809
23938
|
})));
|
|
23810
23939
|
|
|
23811
23940
|
// Allow for horizontal scrolling if content is too wide, which may be
|
|
@@ -24621,8 +24750,7 @@ var Categorizer$1 = {
|
|
|
24621
24750
|
staticTransform: editorProps => {
|
|
24622
24751
|
return _.pick(editorProps, "items", "categories", "values", "randomizeItems");
|
|
24623
24752
|
},
|
|
24624
|
-
isLintable: true
|
|
24625
|
-
getPublicWidgetOptions: getCategorizerPublicWidgetOptions
|
|
24753
|
+
isLintable: true
|
|
24626
24754
|
};
|
|
24627
24755
|
|
|
24628
24756
|
/**
|
|
@@ -24828,8 +24956,7 @@ var CSProgram$1 = {
|
|
|
24828
24956
|
name: "cs-program",
|
|
24829
24957
|
displayName: "CS Program",
|
|
24830
24958
|
widget: CSProgram,
|
|
24831
|
-
hidden: true
|
|
24832
|
-
getPublicWidgetOptions: getCSProgramPublicWidgetOptions
|
|
24959
|
+
hidden: true
|
|
24833
24960
|
};
|
|
24834
24961
|
|
|
24835
24962
|
const getPromptJSON$l = renderProps => {
|
|
@@ -25047,8 +25174,7 @@ var Dropdown$1 = {
|
|
|
25047
25174
|
displayName: "Drop down",
|
|
25048
25175
|
accessible: true,
|
|
25049
25176
|
widget: Dropdown,
|
|
25050
|
-
transform: optionsTransform
|
|
25051
|
-
getPublicWidgetOptions: getDropdownPublicWidgetOptions
|
|
25177
|
+
transform: optionsTransform
|
|
25052
25178
|
};
|
|
25053
25179
|
|
|
25054
25180
|
const getPromptJSON$j = renderProps => {
|
|
@@ -26807,12 +26933,16 @@ class Iframe extends React.Component {
|
|
|
26807
26933
|
// that we need. This makes it a bit safer in case some content
|
|
26808
26934
|
// creator "went wild".
|
|
26809
26935
|
// http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
|
|
26810
|
-
return
|
|
26811
|
-
|
|
26812
|
-
|
|
26813
|
-
|
|
26814
|
-
|
|
26815
|
-
|
|
26936
|
+
return (
|
|
26937
|
+
/*#__PURE__*/
|
|
26938
|
+
// eslint-disable-next-line jsx-a11y/iframe-has-title -- TODO(LEMS-2871): Address a11y error
|
|
26939
|
+
React.createElement("iframe", {
|
|
26940
|
+
sandbox: sandboxProperties,
|
|
26941
|
+
style: style,
|
|
26942
|
+
src: url,
|
|
26943
|
+
allowFullScreen: this.props.allowFullScreen
|
|
26944
|
+
})
|
|
26945
|
+
);
|
|
26816
26946
|
}
|
|
26817
26947
|
}
|
|
26818
26948
|
Iframe.defaultProps = {
|
|
@@ -26844,7 +26974,7 @@ const getPromptJSON$d = renderProps => {
|
|
|
26844
26974
|
|
|
26845
26975
|
const defaultBoxSize = 400;
|
|
26846
26976
|
const defaultRange = [0, 10];
|
|
26847
|
-
const defaultBackgroundImage$
|
|
26977
|
+
const defaultBackgroundImage$1 = {
|
|
26848
26978
|
url: null,
|
|
26849
26979
|
width: 0,
|
|
26850
26980
|
height: 0
|
|
@@ -27001,7 +27131,7 @@ ImageWidget.defaultProps = {
|
|
|
27001
27131
|
title: "",
|
|
27002
27132
|
range: [defaultRange, defaultRange],
|
|
27003
27133
|
box: [defaultBoxSize, defaultBoxSize],
|
|
27004
|
-
backgroundImage: defaultBackgroundImage$
|
|
27134
|
+
backgroundImage: defaultBackgroundImage$1,
|
|
27005
27135
|
labels: [],
|
|
27006
27136
|
alt: "",
|
|
27007
27137
|
caption: "",
|
|
@@ -27618,307 +27748,6 @@ var Interactive = {
|
|
|
27618
27748
|
hidden: true
|
|
27619
27749
|
};
|
|
27620
27750
|
|
|
27621
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
27622
|
-
const defaultBackgroundImage$1 = {
|
|
27623
|
-
url: null
|
|
27624
|
-
};
|
|
27625
|
-
|
|
27626
|
-
/* Style objects */
|
|
27627
|
-
const defaultInstructionsStyle = {
|
|
27628
|
-
fontStyle: "italic",
|
|
27629
|
-
fontWeight: "bold",
|
|
27630
|
-
fontSize: "32px",
|
|
27631
|
-
width: "100%",
|
|
27632
|
-
height: "100%",
|
|
27633
|
-
textAlign: "center",
|
|
27634
|
-
backgroundColor: "white",
|
|
27635
|
-
position: "absolute",
|
|
27636
|
-
zIndex: 1,
|
|
27637
|
-
transition: "opacity .25s ease-in-out"
|
|
27638
|
-
};
|
|
27639
|
-
const instructionsTextStyle = {
|
|
27640
|
-
position: "relative",
|
|
27641
|
-
top: "25%"
|
|
27642
|
-
};
|
|
27643
|
-
function numSteps$1(range, step) {
|
|
27644
|
-
return Math.floor((range[1] - range[0]) / step);
|
|
27645
|
-
}
|
|
27646
|
-
class Graph extends React.Component {
|
|
27647
|
-
constructor(...args) {
|
|
27648
|
-
super(...args);
|
|
27649
|
-
this.graphieDivRef = /*#__PURE__*/React.createRef();
|
|
27650
|
-
this.protractor = void 0;
|
|
27651
|
-
this.ruler = void 0;
|
|
27652
|
-
this._graphie = void 0;
|
|
27653
|
-
// @ts-expect-error - TS2564 - Property '_hasSetupGraphieThisUpdate' has no initializer and is not definitely assigned in the constructor.
|
|
27654
|
-
this._hasSetupGraphieThisUpdate = void 0;
|
|
27655
|
-
// @ts-expect-error - TS2564 - Property '_shouldSetupGraphie' has no initializer and is not definitely assigned in the constructor.
|
|
27656
|
-
this._shouldSetupGraphie = void 0;
|
|
27657
|
-
/* Reset the graphie canvas to its initial state
|
|
27658
|
-
*
|
|
27659
|
-
* Use when re-rendering the parent component and you need a blank
|
|
27660
|
-
* graphie.
|
|
27661
|
-
*/
|
|
27662
|
-
this.reset = () => {
|
|
27663
|
-
this._setupGraphie(false);
|
|
27664
|
-
};
|
|
27665
|
-
this.graphie = () => {
|
|
27666
|
-
return this._graphie;
|
|
27667
|
-
};
|
|
27668
|
-
this.pointsFromNormalized = (coordsList, noSnap) => {
|
|
27669
|
-
const self = this;
|
|
27670
|
-
return _.map(coordsList, function (coords) {
|
|
27671
|
-
return _.map(coords, function (coord, i) {
|
|
27672
|
-
const range = self.props.range[i];
|
|
27673
|
-
if (noSnap) {
|
|
27674
|
-
return range[0] + (range[1] - range[0]) * coord;
|
|
27675
|
-
}
|
|
27676
|
-
const step = self.props.step[i];
|
|
27677
|
-
const nSteps = numSteps$1(range, step);
|
|
27678
|
-
const tick = Math.round(coord * nSteps);
|
|
27679
|
-
return range[0] + step * tick;
|
|
27680
|
-
});
|
|
27681
|
-
});
|
|
27682
|
-
};
|
|
27683
|
-
this._setupGraphie = initialMount => {
|
|
27684
|
-
// Only setupGraphie once per componentDidUpdate().
|
|
27685
|
-
// This prevents this component from rendering graphie
|
|
27686
|
-
// and then immediately re-render graphie because its
|
|
27687
|
-
// parent component asked it to. This will happen when
|
|
27688
|
-
// props on the parent and props on this component both
|
|
27689
|
-
// require graphie to be re-rendered.
|
|
27690
|
-
if (this._hasSetupGraphieThisUpdate) {
|
|
27691
|
-
return;
|
|
27692
|
-
}
|
|
27693
|
-
const graphieDiv = this.graphieDivRef.current;
|
|
27694
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'.
|
|
27695
|
-
$(graphieDiv).empty();
|
|
27696
|
-
|
|
27697
|
-
// Content creators may need to explicitly add the dollar signs so the
|
|
27698
|
-
// strings are picked up by our translation tools. However, these math
|
|
27699
|
-
// annotations are redundant because we already render all graph labels
|
|
27700
|
-
// in math mode. For example, a label value of `$\text{Dollars}$` will
|
|
27701
|
-
// be translatable, but we only want to pass the string
|
|
27702
|
-
// `\text{Dollars}` to the Graph widget.
|
|
27703
|
-
const labels = this.props.labels.map(label => Util.unescapeMathMode(label));
|
|
27704
|
-
const range = this.props.range;
|
|
27705
|
-
// @ts-expect-error - TS2345: Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'.
|
|
27706
|
-
const graphie = this._graphie = GraphUtils.createGraphie(graphieDiv);
|
|
27707
|
-
const gridConfig = this._getGridConfig();
|
|
27708
|
-
// @ts-expect-error - TS2339: Property 'snap' does not exist on type 'Graphie'.
|
|
27709
|
-
graphie.snap = this.props.snapStep;
|
|
27710
|
-
if (this.props.markings === "graph") {
|
|
27711
|
-
graphie.graphInit({
|
|
27712
|
-
range: range,
|
|
27713
|
-
scale: vector$3.map(gridConfig, g => g.scale),
|
|
27714
|
-
axisArrows: "<->",
|
|
27715
|
-
labelFormat: function (s) {
|
|
27716
|
-
return "\\small{" + s + "}";
|
|
27717
|
-
},
|
|
27718
|
-
gridStep: this.props.gridStep,
|
|
27719
|
-
tickStep: vector$3.map(gridConfig, g => g.tickStep),
|
|
27720
|
-
labelStep: 1,
|
|
27721
|
-
unityLabels: vector$3.map(gridConfig, g => g.unityLabel),
|
|
27722
|
-
isMobile: this.props.isMobile
|
|
27723
|
-
});
|
|
27724
|
-
graphie.label([0, range[1][1]], labels[1], this.props.isMobile ? "below right" : "above");
|
|
27725
|
-
graphie.label([range[0][1], 0], labels[0], this.props.isMobile ? "above left" : "right");
|
|
27726
|
-
} else if (this.props.markings === "grid") {
|
|
27727
|
-
graphie.graphInit({
|
|
27728
|
-
range: range,
|
|
27729
|
-
scale: vector$3.map(gridConfig, g => g.scale),
|
|
27730
|
-
gridStep: this.props.gridStep,
|
|
27731
|
-
axes: false,
|
|
27732
|
-
ticks: false,
|
|
27733
|
-
labels: false,
|
|
27734
|
-
isMobile: this.props.isMobile
|
|
27735
|
-
});
|
|
27736
|
-
} else if (this.props.markings === "none") {
|
|
27737
|
-
graphie.init({
|
|
27738
|
-
range: range,
|
|
27739
|
-
scale: vector$3.map(gridConfig, g => g.scale),
|
|
27740
|
-
isMobile: this.props.isMobile
|
|
27741
|
-
});
|
|
27742
|
-
}
|
|
27743
|
-
|
|
27744
|
-
// Add instructions just before mouse layer
|
|
27745
|
-
const visible = 0.5;
|
|
27746
|
-
const invisible = 0.0;
|
|
27747
|
-
let $instructionsWrapper;
|
|
27748
|
-
if (this.props.instructions) {
|
|
27749
|
-
$instructionsWrapper = $("<div/>");
|
|
27750
|
-
_.each(defaultInstructionsStyle, function (value, key) {
|
|
27751
|
-
$instructionsWrapper.css(key, value);
|
|
27752
|
-
});
|
|
27753
|
-
$instructionsWrapper.css("opacity", visible);
|
|
27754
|
-
const $instructions = $("<span/>", {
|
|
27755
|
-
text: this.props.instructions
|
|
27756
|
-
});
|
|
27757
|
-
_.each(instructionsTextStyle, function (value, key) {
|
|
27758
|
-
$instructions.css(key, value);
|
|
27759
|
-
});
|
|
27760
|
-
$instructionsWrapper.append($instructions);
|
|
27761
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'append' does not exist on type 'JQueryStatic'.
|
|
27762
|
-
$(graphieDiv).append($instructionsWrapper);
|
|
27763
|
-
} else {
|
|
27764
|
-
$instructionsWrapper = undefined;
|
|
27765
|
-
}
|
|
27766
|
-
|
|
27767
|
-
// Add some handlers for instructions text (if necessary)
|
|
27768
|
-
const onMouseDown = $instructionsWrapper || this.props.onMouseDown ? _.bind(function (coord) {
|
|
27769
|
-
if ($instructionsWrapper) {
|
|
27770
|
-
$instructionsWrapper.remove();
|
|
27771
|
-
$instructionsWrapper = null;
|
|
27772
|
-
}
|
|
27773
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
27774
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
27775
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
27776
|
-
this.props.onMouseDown(coord);
|
|
27777
|
-
}, this) : undefined;
|
|
27778
|
-
const onMouseOver = $instructionsWrapper ? function () {
|
|
27779
|
-
var _$instructionsWrapper;
|
|
27780
|
-
(_$instructionsWrapper = $instructionsWrapper) == null || _$instructionsWrapper.css("opacity", invisible);
|
|
27781
|
-
} : undefined;
|
|
27782
|
-
const onMouseOut = $instructionsWrapper ? function () {
|
|
27783
|
-
var _$instructionsWrapper2;
|
|
27784
|
-
(_$instructionsWrapper2 = $instructionsWrapper) == null || _$instructionsWrapper2.css("opacity", visible);
|
|
27785
|
-
} : undefined;
|
|
27786
|
-
/* eslint-enable indent */
|
|
27787
|
-
|
|
27788
|
-
graphie.addMouseLayer({
|
|
27789
|
-
onClick: this.props.onClick,
|
|
27790
|
-
onMouseDown: onMouseDown,
|
|
27791
|
-
onMouseOver: onMouseOver,
|
|
27792
|
-
onMouseOut: onMouseOut,
|
|
27793
|
-
onMouseMove: this.props.onMouseMove,
|
|
27794
|
-
allowScratchpad: true,
|
|
27795
|
-
setDrawingAreaAvailable: this.props.setDrawingAreaAvailable
|
|
27796
|
-
});
|
|
27797
|
-
this._updateProtractor();
|
|
27798
|
-
this._updateRuler();
|
|
27799
|
-
|
|
27800
|
-
// We set this flag before jumping into our callback
|
|
27801
|
-
// to avoid recursing if our callback calls reset() itself
|
|
27802
|
-
this._hasSetupGraphieThisUpdate = true;
|
|
27803
|
-
if (!initialMount && this.props.onGraphieUpdated) {
|
|
27804
|
-
// Calling a parent callback in componentDidMount is bad and
|
|
27805
|
-
// results in hard-to-reason-about lifecycle problems (esp. with
|
|
27806
|
-
// refs), so we do it only on update and rely on the parent to
|
|
27807
|
-
// query for the graphie object on initial mount
|
|
27808
|
-
this.props.onGraphieUpdated(graphie);
|
|
27809
|
-
}
|
|
27810
|
-
};
|
|
27811
|
-
this._getGridConfig = () => {
|
|
27812
|
-
return vector$3.map(this.props.step, (step, i) => Util.gridDimensionConfig(step, this.props.range[i], this.props.box[i], this.props.gridStep[i]));
|
|
27813
|
-
};
|
|
27814
|
-
this._updateProtractor = () => {
|
|
27815
|
-
if (this.protractor) {
|
|
27816
|
-
this.protractor.remove();
|
|
27817
|
-
}
|
|
27818
|
-
if (this.props.showProtractor) {
|
|
27819
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
27820
|
-
const coord = this.pointsFromNormalized([[0.5, 0.05]])[0];
|
|
27821
|
-
this.protractor = this._graphie.protractor(coord);
|
|
27822
|
-
}
|
|
27823
|
-
};
|
|
27824
|
-
this._updateRuler = () => {
|
|
27825
|
-
if (this.ruler) {
|
|
27826
|
-
this.ruler.remove();
|
|
27827
|
-
}
|
|
27828
|
-
if (this.props.showRuler) {
|
|
27829
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
27830
|
-
const coord = this.pointsFromNormalized([[0.5, 0.25]])[0];
|
|
27831
|
-
const extent = this._graphie.range[0][1] - this._graphie.range[0][0];
|
|
27832
|
-
this.ruler = this._graphie.ruler({
|
|
27833
|
-
center: coord,
|
|
27834
|
-
label: this.props.rulerLabel,
|
|
27835
|
-
pixelsPerUnit: this._graphie.scale[0],
|
|
27836
|
-
ticksPerUnit: this.props.rulerTicks,
|
|
27837
|
-
units: Math.round(0.8 * extent)
|
|
27838
|
-
});
|
|
27839
|
-
}
|
|
27840
|
-
};
|
|
27841
|
-
this.toJSON = () => {
|
|
27842
|
-
return _.pick(this.props, "range", "step", "markings", "labels", "backgroundImage", "showProtractor", "showRuler", "rulerLabel", "rulerTicks", "gridStep", "snapStep");
|
|
27843
|
-
};
|
|
27844
|
-
}
|
|
27845
|
-
componentDidMount() {
|
|
27846
|
-
this._setupGraphie(true);
|
|
27847
|
-
}
|
|
27848
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
27849
|
-
const potentialChanges = ["labels", "range", "step", "markings", "showProtractor", "showRuler", "rulerLabel", "rulerTicks", "gridStep", "snapStep"];
|
|
27850
|
-
const self = this;
|
|
27851
|
-
_.each(potentialChanges, function (prop) {
|
|
27852
|
-
if (!_.isEqual(self.props[prop], nextProps[prop])) {
|
|
27853
|
-
self._shouldSetupGraphie = true;
|
|
27854
|
-
}
|
|
27855
|
-
});
|
|
27856
|
-
}
|
|
27857
|
-
componentDidUpdate() {
|
|
27858
|
-
// Only setupGraphie once per componentDidUpdate().
|
|
27859
|
-
// See explanation in setupGraphie().
|
|
27860
|
-
this._hasSetupGraphieThisUpdate = false;
|
|
27861
|
-
if (this._shouldSetupGraphie) {
|
|
27862
|
-
this._setupGraphie(false);
|
|
27863
|
-
this._shouldSetupGraphie = false;
|
|
27864
|
-
}
|
|
27865
|
-
}
|
|
27866
|
-
render() {
|
|
27867
|
-
let image;
|
|
27868
|
-
const imageData = this.props.backgroundImage;
|
|
27869
|
-
if (imageData.url) {
|
|
27870
|
-
const scale = this.props.box[0] / interactiveSizes$1.defaultBoxSize;
|
|
27871
|
-
image = /*#__PURE__*/React.createElement(context$1.Consumer, null, ({
|
|
27872
|
-
setAssetStatus
|
|
27873
|
-
}) => /*#__PURE__*/React.createElement(SvgImage
|
|
27874
|
-
// @ts-expect-error - TS2741 - Property 'alt' is missing in type '{ src: any; width: any; height: any; scale: number; responsive: false; }' but required in type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "children" | "height" | "width" | "title" | "alt" | "trackInteraction" | "preloader" | "allowFullBleed" | "extraGraphie" | "overrideAriaHidden">'.
|
|
27875
|
-
, {
|
|
27876
|
-
src: imageData.url,
|
|
27877
|
-
width: imageData.width,
|
|
27878
|
-
height: imageData.height,
|
|
27879
|
-
scale: scale,
|
|
27880
|
-
responsive: false,
|
|
27881
|
-
setAssetStatus: setAssetStatus
|
|
27882
|
-
}));
|
|
27883
|
-
} else {
|
|
27884
|
-
image = null;
|
|
27885
|
-
}
|
|
27886
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
27887
|
-
className: "graphie-container blank-background",
|
|
27888
|
-
style: {
|
|
27889
|
-
width: this.props.box[0],
|
|
27890
|
-
height: this.props.box[1]
|
|
27891
|
-
}
|
|
27892
|
-
// @ts-expect-error - TS2339 - Property 'onMouseOut' does not exist on type 'Graph'.
|
|
27893
|
-
,
|
|
27894
|
-
onMouseOut: this.onMouseOut
|
|
27895
|
-
// @ts-expect-error - TS2339 - Property 'onMouseOver' does not exist on type 'Graph'.
|
|
27896
|
-
,
|
|
27897
|
-
onMouseOver: this.onMouseOver
|
|
27898
|
-
// @ts-expect-error - TS2339 - Property 'onClick' does not exist on type 'Graph'.
|
|
27899
|
-
,
|
|
27900
|
-
onClick: this.onClick
|
|
27901
|
-
}, image, /*#__PURE__*/React.createElement("div", {
|
|
27902
|
-
className: "graphie",
|
|
27903
|
-
ref: this.graphieDivRef
|
|
27904
|
-
}));
|
|
27905
|
-
}
|
|
27906
|
-
}
|
|
27907
|
-
Graph.defaultProps = {
|
|
27908
|
-
labels: ["x", "y"],
|
|
27909
|
-
range: [[-10, 10], [-10, 10]],
|
|
27910
|
-
step: [1, 1],
|
|
27911
|
-
gridStep: [1, 1],
|
|
27912
|
-
snapStep: [0.5, 0.5],
|
|
27913
|
-
markings: "graph",
|
|
27914
|
-
backgroundImage: defaultBackgroundImage$1,
|
|
27915
|
-
showProtractor: false,
|
|
27916
|
-
showRuler: false,
|
|
27917
|
-
rulerLabel: "",
|
|
27918
|
-
rulerTicks: 10,
|
|
27919
|
-
isMobile: false
|
|
27920
|
-
};
|
|
27921
|
-
|
|
27922
27751
|
const getPromptJSON$b = (props, userInput) => {
|
|
27923
27752
|
var _props$backgroundImag;
|
|
27924
27753
|
return {
|
|
@@ -28471,13 +28300,18 @@ function escapeIfUnescaped(character) {
|
|
|
28471
28300
|
}
|
|
28472
28301
|
}
|
|
28473
28302
|
|
|
28474
|
-
function AxisLabels(
|
|
28303
|
+
function AxisLabels({
|
|
28304
|
+
i18n
|
|
28305
|
+
}) {
|
|
28475
28306
|
const {
|
|
28476
28307
|
range,
|
|
28477
28308
|
labels,
|
|
28478
28309
|
width,
|
|
28479
28310
|
height
|
|
28480
28311
|
} = useGraphConfig();
|
|
28312
|
+
const {
|
|
28313
|
+
strings
|
|
28314
|
+
} = i18n;
|
|
28481
28315
|
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28482
28316
|
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28483
28317
|
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
@@ -28495,6 +28329,7 @@ function AxisLabels() {
|
|
|
28495
28329
|
TeX
|
|
28496
28330
|
} = getDependencies();
|
|
28497
28331
|
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
|
|
28332
|
+
"aria-label": strings.xAxis,
|
|
28498
28333
|
style: {
|
|
28499
28334
|
position: "absolute",
|
|
28500
28335
|
left: x1,
|
|
@@ -28503,6 +28338,7 @@ function AxisLabels() {
|
|
|
28503
28338
|
transform: "translate(7px, -50%)"
|
|
28504
28339
|
}
|
|
28505
28340
|
}, /*#__PURE__*/React__default.createElement(TeX, null, replaceOutsideTeX(xAxisLabelText))), /*#__PURE__*/React__default.createElement("span", {
|
|
28341
|
+
"aria-label": strings.yAxis,
|
|
28506
28342
|
style: {
|
|
28507
28343
|
position: "absolute",
|
|
28508
28344
|
left: x2,
|
|
@@ -29312,7 +29148,6 @@ RangeInput.defaultProps = {
|
|
|
29312
29148
|
var components = /*#__PURE__*/Object.freeze({
|
|
29313
29149
|
__proto__: null,
|
|
29314
29150
|
ButtonGroup: ButtonGroup,
|
|
29315
|
-
Graph: Graph,
|
|
29316
29151
|
Graphie: Graphie,
|
|
29317
29152
|
HUD: HUD,
|
|
29318
29153
|
Icon: Icon,
|
|
@@ -29453,9 +29288,9 @@ const LockedFunction = props => {
|
|
|
29453
29288
|
const {
|
|
29454
29289
|
color,
|
|
29455
29290
|
strokeStyle,
|
|
29456
|
-
directionalAxis
|
|
29457
|
-
domain
|
|
29291
|
+
directionalAxis
|
|
29458
29292
|
} = props;
|
|
29293
|
+
const domain = props.domain ? [Number.isFinite(props.domain[0]) ? props.domain[0] : -Infinity, Number.isFinite(props.domain[1]) ? props.domain[1] : Infinity] : undefined;
|
|
29459
29294
|
const plotProps = {
|
|
29460
29295
|
color: lockedFigureColors[color],
|
|
29461
29296
|
style: strokeStyle,
|
|
@@ -29534,14 +29369,90 @@ function Vector(props) {
|
|
|
29534
29369
|
}));
|
|
29535
29370
|
}
|
|
29536
29371
|
|
|
29537
|
-
function srFormatNumber(a, locale) {
|
|
29372
|
+
function srFormatNumber(a, locale, maximumFractionDigits) {
|
|
29373
|
+
const piBasedNumber = getPiMultiple(a);
|
|
29374
|
+
if (piBasedNumber) {
|
|
29375
|
+
return piBasedNumber;
|
|
29376
|
+
}
|
|
29377
|
+
|
|
29538
29378
|
// adding zero here converts negative zero to positive zero.
|
|
29539
29379
|
return (0 + a).toLocaleString(locale, {
|
|
29540
|
-
maximumFractionDigits: 3,
|
|
29380
|
+
maximumFractionDigits: maximumFractionDigits != null ? maximumFractionDigits : 3,
|
|
29541
29381
|
useGrouping: false // no thousands separators
|
|
29542
29382
|
});
|
|
29543
29383
|
}
|
|
29544
29384
|
|
|
29385
|
+
// Exported for testing
|
|
29386
|
+
function getPiMultiple(a) {
|
|
29387
|
+
// - Save some calculations by checking if this is an integer first.
|
|
29388
|
+
// If a is an integer, it's definitely not a multiple of π.
|
|
29389
|
+
// - 0 should not be presented as a multiple of π.
|
|
29390
|
+
// - If a is greater than 1e12, then it thinks that it's always a
|
|
29391
|
+
// multiple of π even when it's not, due to rounding errors.
|
|
29392
|
+
if (Number.isInteger(a) || a === 0 || a > 1e12) {
|
|
29393
|
+
return null;
|
|
29394
|
+
}
|
|
29395
|
+
|
|
29396
|
+
// Figure out the coefficient before the π.
|
|
29397
|
+
// Example: If a = π/2, then piCoefficient = 0.5
|
|
29398
|
+
// Example: If a = 2π, then piCoefficient = 2
|
|
29399
|
+
const piCoefficient = a / Math.PI;
|
|
29400
|
+
// Truncate the coefficient to account for floating point errors.
|
|
29401
|
+
// π calculations are only accurate up to precision of 1e-12.
|
|
29402
|
+
const truncatedCoefficient = parseFloat(piCoefficient.toFixed(12));
|
|
29403
|
+
|
|
29404
|
+
// If the coefficient is already an integer, then the number
|
|
29405
|
+
// is a multiple of π. Return it here.
|
|
29406
|
+
// Example: If a = 2π, then truncatedCoefficient = 2, so return "2π"
|
|
29407
|
+
if (Number.isInteger(truncatedCoefficient)) {
|
|
29408
|
+
// Return "π" rather than "1π".
|
|
29409
|
+
if (truncatedCoefficient === 1) {
|
|
29410
|
+
return "π";
|
|
29411
|
+
}
|
|
29412
|
+
|
|
29413
|
+
// Return "-π" rather than "-1π".
|
|
29414
|
+
if (truncatedCoefficient === -1) {
|
|
29415
|
+
return "-π";
|
|
29416
|
+
}
|
|
29417
|
+
return truncatedCoefficient + "π";
|
|
29418
|
+
}
|
|
29419
|
+
|
|
29420
|
+
// If the coefficient is not an integer, then we need to
|
|
29421
|
+
// check if it's a multiple of π/2, π/3, π/4, or π/6.
|
|
29422
|
+
// These are π-based values on a unit circle.
|
|
29423
|
+
const acceptableDivisors = [2, 3, 4, 6];
|
|
29424
|
+
|
|
29425
|
+
// Loop through the acceptable divisors and check if the
|
|
29426
|
+
// coefficient is a multiple of (1/divisor).
|
|
29427
|
+
// Example: If a = 5π/6, the coeff is 5/6, which is a multiple of 1/6.
|
|
29428
|
+
// So we return "5π/6".
|
|
29429
|
+
for (const divisor of acceptableDivisors) {
|
|
29430
|
+
// Check if the coefficient is a multiple of (1/divisor) by
|
|
29431
|
+
// multiplying the coefficient by the divisor and checking if
|
|
29432
|
+
// the result is an integer.
|
|
29433
|
+
// Example: If a = 5π/6, then piCoefficient = 5/6. We multiply
|
|
29434
|
+
// by 6 (divisor) to get 5, which is an integer.
|
|
29435
|
+
const coefficientNumerator = parseFloat(
|
|
29436
|
+
// π calculations are only accurate up to precision of 1e-12.
|
|
29437
|
+
(piCoefficient * divisor).toFixed(12));
|
|
29438
|
+
if (Number.isInteger(coefficientNumerator)) {
|
|
29439
|
+
// Handle the case where the coefficient numberator is just 1.
|
|
29440
|
+
// We don't want to write "π/6" as "1π/6"
|
|
29441
|
+
if (coefficientNumerator === 1) {
|
|
29442
|
+
return "π/" + divisor;
|
|
29443
|
+
}
|
|
29444
|
+
|
|
29445
|
+
// Handle the case where the coefficient numberator is just -1.
|
|
29446
|
+
// We don't want to write "π/6" as "-1π/6"
|
|
29447
|
+
if (coefficientNumerator === -1) {
|
|
29448
|
+
return "-π/" + divisor;
|
|
29449
|
+
}
|
|
29450
|
+
return coefficientNumerator + "π/" + divisor;
|
|
29451
|
+
}
|
|
29452
|
+
}
|
|
29453
|
+
return null;
|
|
29454
|
+
}
|
|
29455
|
+
|
|
29545
29456
|
/**
|
|
29546
29457
|
* Given a ray and a rectangular box, find the point where the ray intersects
|
|
29547
29458
|
* the edge of the box. Assumes the `initialPoint` is inside the box.
|
|
@@ -29713,6 +29624,95 @@ a, b, c) {
|
|
|
29713
29624
|
return [x1, x2];
|
|
29714
29625
|
}
|
|
29715
29626
|
|
|
29627
|
+
/**
|
|
29628
|
+
* Given a list of points, return the angle made by the point at index i
|
|
29629
|
+
* with the points at indices i - 1 and i + 1.
|
|
29630
|
+
*/
|
|
29631
|
+
function getAngleFromPoints(points, i) {
|
|
29632
|
+
// Make sure the index is valid.
|
|
29633
|
+
if (i < 0 || i >= points.length || !Number.isInteger(i)) {
|
|
29634
|
+
return null;
|
|
29635
|
+
}
|
|
29636
|
+
|
|
29637
|
+
// If there are fewer than 3 points, there is no angle.
|
|
29638
|
+
if (points.length < 3) {
|
|
29639
|
+
return null;
|
|
29640
|
+
}
|
|
29641
|
+
|
|
29642
|
+
// Get the points at the current index and the two adjacent points.
|
|
29643
|
+
const point = points.at(i);
|
|
29644
|
+
const pt1 = points.at(i - 1);
|
|
29645
|
+
const pt2 = points[(i + 1) % points.length];
|
|
29646
|
+
if (!point || !pt1 || !pt2) {
|
|
29647
|
+
return null;
|
|
29648
|
+
}
|
|
29649
|
+
|
|
29650
|
+
// Get the lengths of adjacent sides, making a triangle
|
|
29651
|
+
// we can use to calculate the angle.
|
|
29652
|
+
const a = vec.dist(point, pt1);
|
|
29653
|
+
const b = vec.dist(point, pt2);
|
|
29654
|
+
const c = vec.dist(pt1, pt2);
|
|
29655
|
+
|
|
29656
|
+
// Law of cosines
|
|
29657
|
+
const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
|
|
29658
|
+
return angle;
|
|
29659
|
+
}
|
|
29660
|
+
function getSideLengthsFromPoints(points, i, isPolygonOpen) {
|
|
29661
|
+
if (i < 0 || i >= points.length || !Number.isInteger(i)) {
|
|
29662
|
+
return [];
|
|
29663
|
+
}
|
|
29664
|
+
if (points.length < 2) {
|
|
29665
|
+
return [];
|
|
29666
|
+
}
|
|
29667
|
+
const returnArray = [];
|
|
29668
|
+
const point = points[i];
|
|
29669
|
+
// If this point is the first point, then side 1 is the
|
|
29670
|
+
// last point in the list.
|
|
29671
|
+
const point1Index = i === 0 ? points.length - 1 : i - 1;
|
|
29672
|
+
const point1 = points[point1Index];
|
|
29673
|
+
// Make sure the previous point is not the same
|
|
29674
|
+
// as the current point.
|
|
29675
|
+
const side1 = i !== point1Index ? vec.dist(point, point1) : null;
|
|
29676
|
+
// The first side of the first point does not exist if
|
|
29677
|
+
// the polygon is open.
|
|
29678
|
+
if (side1 && !(isPolygonOpen && i === 0)) {
|
|
29679
|
+
returnArray.push({
|
|
29680
|
+
pointIndex: point1Index,
|
|
29681
|
+
sideLength: side1
|
|
29682
|
+
});
|
|
29683
|
+
}
|
|
29684
|
+
|
|
29685
|
+
// See if there is a side 2.
|
|
29686
|
+
const point2Index = (i + 1) % points.length;
|
|
29687
|
+
const point2 = points[point2Index];
|
|
29688
|
+
// Make sure that the next point is not the same as the
|
|
29689
|
+
// current point, and don't repeat the first point.
|
|
29690
|
+
const side2 = i !== point2Index && point2Index !== point1Index ? vec.dist(point, point2) : null;
|
|
29691
|
+
// The second side of the last point does not exist if
|
|
29692
|
+
// the polygon is open.
|
|
29693
|
+
if (side2 && !(isPolygonOpen && i === points.length - 1)) {
|
|
29694
|
+
returnArray.push({
|
|
29695
|
+
pointIndex: point2Index,
|
|
29696
|
+
sideLength: side2
|
|
29697
|
+
});
|
|
29698
|
+
}
|
|
29699
|
+
return returnArray;
|
|
29700
|
+
}
|
|
29701
|
+
|
|
29702
|
+
/**
|
|
29703
|
+
* Determine whether to determine the string with the
|
|
29704
|
+
* exact side length or the approximate side length.
|
|
29705
|
+
*/
|
|
29706
|
+
function getPolygonSideString(sideLength, pointIndex, strings, locale) {
|
|
29707
|
+
return Number.isInteger(sideLength) ? strings.srPolygonSideLength({
|
|
29708
|
+
pointNum: pointIndex + 1,
|
|
29709
|
+
length: `${sideLength}`
|
|
29710
|
+
}) : strings.srPolygonSideLengthApprox({
|
|
29711
|
+
pointNum: pointIndex + 1,
|
|
29712
|
+
length: srFormatNumber(sideLength, locale, 1)
|
|
29713
|
+
});
|
|
29714
|
+
}
|
|
29715
|
+
|
|
29716
29716
|
const {
|
|
29717
29717
|
calculateAngleInDegrees: calculateAngleInDegrees$2
|
|
29718
29718
|
} = angles;
|
|
@@ -30158,7 +30158,7 @@ const SvgDefs = () => /*#__PURE__*/React__default.createElement("defs", null, /*
|
|
|
30158
30158
|
})));
|
|
30159
30159
|
|
|
30160
30160
|
const {
|
|
30161
|
-
clockwise
|
|
30161
|
+
clockwise
|
|
30162
30162
|
} = geometry;
|
|
30163
30163
|
const {
|
|
30164
30164
|
getAngleFromVertex: getAngleFromVertex$1
|
|
@@ -30172,7 +30172,7 @@ const PolygonAngle = ({
|
|
|
30172
30172
|
snapTo
|
|
30173
30173
|
}) => {
|
|
30174
30174
|
const [centerX, centerY] = centerPoint;
|
|
30175
|
-
const areClockwise = clockwise
|
|
30175
|
+
const areClockwise = clockwise([centerPoint, ...endPoints]);
|
|
30176
30176
|
const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
|
|
30177
30177
|
const radius = 0.3;
|
|
30178
30178
|
const a = vec.dist(centerPoint, endPoints[0]);
|
|
@@ -30244,7 +30244,7 @@ const Angle = ({
|
|
|
30244
30244
|
range
|
|
30245
30245
|
}) => {
|
|
30246
30246
|
// Check if the points are clockwise or not, depending on whether we allow reflex angles
|
|
30247
|
-
const areClockwise = clockwise
|
|
30247
|
+
const areClockwise = clockwise([...coords, vertex]);
|
|
30248
30248
|
const shouldReverseCoords = areClockwise && !allowReflexAngles;
|
|
30249
30249
|
|
|
30250
30250
|
// Reverse the coordinates accordingly to ensure the angle is calculated correctly
|
|
@@ -30608,6 +30608,34 @@ function range(min, max, step = 1) {
|
|
|
30608
30608
|
return result;
|
|
30609
30609
|
}
|
|
30610
30610
|
|
|
30611
|
+
function Hairlines(props) {
|
|
30612
|
+
const {
|
|
30613
|
+
point
|
|
30614
|
+
} = props;
|
|
30615
|
+
const {
|
|
30616
|
+
range
|
|
30617
|
+
} = useGraphConfig();
|
|
30618
|
+
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
30619
|
+
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
30620
|
+
const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
|
|
30621
|
+
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
30622
|
+
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
30623
|
+
const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
|
|
30624
|
+
return /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("line", {
|
|
30625
|
+
x1: verticalStartX,
|
|
30626
|
+
y1: y,
|
|
30627
|
+
x2: verticalEndX,
|
|
30628
|
+
y2: y,
|
|
30629
|
+
stroke: color.blue
|
|
30630
|
+
}), /*#__PURE__*/React.createElement("line", {
|
|
30631
|
+
x1: x,
|
|
30632
|
+
y1: horizontalStartY,
|
|
30633
|
+
x2: x,
|
|
30634
|
+
y2: horizontalEndY,
|
|
30635
|
+
stroke: color.blue
|
|
30636
|
+
}));
|
|
30637
|
+
}
|
|
30638
|
+
|
|
30611
30639
|
// The hitbox size of 48px by 48px is preserved from the legacy interactive
|
|
30612
30640
|
// graph.
|
|
30613
30641
|
const hitboxSizePx = 48;
|
|
@@ -30618,7 +30646,6 @@ const hitboxSizePx = 48;
|
|
|
30618
30646
|
const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
30619
30647
|
var _Object$entries$find$, _Object$entries$find;
|
|
30620
30648
|
const {
|
|
30621
|
-
range,
|
|
30622
30649
|
markings,
|
|
30623
30650
|
showTooltips
|
|
30624
30651
|
} = useGraphConfig();
|
|
@@ -30626,6 +30653,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
30626
30653
|
point,
|
|
30627
30654
|
color: color$1 = color.blue,
|
|
30628
30655
|
dragging,
|
|
30656
|
+
focused,
|
|
30629
30657
|
cursor,
|
|
30630
30658
|
showFocusRing,
|
|
30631
30659
|
onClick = () => {}
|
|
@@ -30636,25 +30664,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
30636
30664
|
const wbColorName = (_Object$entries$find$ = (_Object$entries$find = Object.entries(color).find(([_, value]) => value === color$1)) == null ? void 0 : _Object$entries$find[0]) != null ? _Object$entries$find$ : "blue";
|
|
30637
30665
|
const pointClasses = classNames("movable-point", dragging && "movable-point--dragging", showFocusRing && "movable-point--focus");
|
|
30638
30666
|
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
30639
|
-
const
|
|
30640
|
-
const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
|
|
30641
|
-
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
30642
|
-
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
30643
|
-
const [[__, horizontalEndY]] = useTransformVectorsToPixels([0, yMax]);
|
|
30644
|
-
const showHairlines = dragging && markings !== "none";
|
|
30645
|
-
const hairlines = /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("line", {
|
|
30646
|
-
x1: verticalStartX,
|
|
30647
|
-
y1: y,
|
|
30648
|
-
x2: verticalEndX,
|
|
30649
|
-
y2: y,
|
|
30650
|
-
stroke: color$1
|
|
30651
|
-
}), /*#__PURE__*/React.createElement("line", {
|
|
30652
|
-
x1: x,
|
|
30653
|
-
y1: horizontalStartY,
|
|
30654
|
-
x2: x,
|
|
30655
|
-
y2: horizontalEndY,
|
|
30656
|
-
stroke: color$1
|
|
30657
|
-
}));
|
|
30667
|
+
const showHairlines = (dragging || focused) && markings !== "none";
|
|
30658
30668
|
const svgForPoint = /*#__PURE__*/React.createElement("g", {
|
|
30659
30669
|
ref: hitboxRef,
|
|
30660
30670
|
className: pointClasses,
|
|
@@ -30690,7 +30700,9 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
30690
30700
|
},
|
|
30691
30701
|
"data-testid": "movable-point__center"
|
|
30692
30702
|
}));
|
|
30693
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines &&
|
|
30703
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines && /*#__PURE__*/React.createElement(Hairlines, {
|
|
30704
|
+
point: point
|
|
30705
|
+
}), showTooltips ? /*#__PURE__*/React.createElement(Tooltip$1, {
|
|
30694
30706
|
autoUpdate: true,
|
|
30695
30707
|
backgroundColor: wbColorName,
|
|
30696
30708
|
content: `(${point[X]}, ${point[Y]})`,
|
|
@@ -30784,6 +30796,7 @@ function useControlPoint(params) {
|
|
|
30784
30796
|
},
|
|
30785
30797
|
point: point,
|
|
30786
30798
|
dragging: dragging,
|
|
30799
|
+
focused: focused,
|
|
30787
30800
|
color: color,
|
|
30788
30801
|
ref: visiblePointRef,
|
|
30789
30802
|
showFocusRing: focused
|
|
@@ -31000,13 +31013,13 @@ const {
|
|
|
31000
31013
|
getClockwiseAngle: getClockwiseAngle$2,
|
|
31001
31014
|
polar: polar$1
|
|
31002
31015
|
} = angles;
|
|
31003
|
-
function renderAngleGraph(state, dispatch) {
|
|
31016
|
+
function renderAngleGraph(state, dispatch, i18n) {
|
|
31004
31017
|
return {
|
|
31005
31018
|
graph: /*#__PURE__*/React.createElement(AngleGraph, {
|
|
31006
31019
|
graphState: state,
|
|
31007
31020
|
dispatch: dispatch
|
|
31008
31021
|
}),
|
|
31009
|
-
interactiveElementsDescription:
|
|
31022
|
+
interactiveElementsDescription: getAngleGraphDescription(state, i18n)
|
|
31010
31023
|
};
|
|
31011
31024
|
}
|
|
31012
31025
|
function AngleGraph(props) {
|
|
@@ -31017,6 +31030,9 @@ function AngleGraph(props) {
|
|
|
31017
31030
|
const {
|
|
31018
31031
|
graphDimensionsInPixels
|
|
31019
31032
|
} = useGraphConfig();
|
|
31033
|
+
const i18n = usePerseusI18n();
|
|
31034
|
+
const id = React.useId();
|
|
31035
|
+
const descriptionId = id + "-description";
|
|
31020
31036
|
const {
|
|
31021
31037
|
coords,
|
|
31022
31038
|
showAngles,
|
|
@@ -31065,76 +31081,106 @@ function AngleGraph(props) {
|
|
|
31065
31081
|
showAngles: showAngles || false // Whether to show the angle or not
|
|
31066
31082
|
};
|
|
31067
31083
|
|
|
31084
|
+
// Aria strings
|
|
31068
31085
|
const {
|
|
31069
|
-
|
|
31070
|
-
|
|
31071
|
-
|
|
31072
|
-
|
|
31073
|
-
|
|
31074
|
-
|
|
31075
|
-
angleMeasure,
|
|
31076
|
-
vertexX: srFormatNumber(coords[1][X], locale),
|
|
31077
|
-
vertexY: srFormatNumber(coords[1][Y], locale),
|
|
31078
|
-
startingSideX: srFormatNumber(coords[2][X], locale),
|
|
31079
|
-
startingSideY: srFormatNumber(coords[2][Y], locale),
|
|
31080
|
-
endingSideX: srFormatNumber(coords[0][X], locale),
|
|
31081
|
-
endingSideY: srFormatNumber(coords[0][Y], locale)
|
|
31082
|
-
});
|
|
31083
|
-
const formatCoordinates = (x, y) => ({
|
|
31084
|
-
x: srFormatNumber(x, locale),
|
|
31085
|
-
y: srFormatNumber(y, locale)
|
|
31086
|
-
});
|
|
31087
|
-
const formatSideAriaLabel = (side, newPoint) => {
|
|
31088
|
-
const formattedCoordinates = formatCoordinates(newPoint[X], newPoint[Y]);
|
|
31089
|
-
const label = side === "terminal" ? strings.srAngleSideAtCoordinates(_extends({
|
|
31090
|
-
point: 1,
|
|
31091
|
-
side: "terminal side"
|
|
31092
|
-
}, formattedCoordinates)) : strings.srAngleSideAtCoordinates(_extends({
|
|
31093
|
-
point: 3,
|
|
31094
|
-
side: "initial side"
|
|
31095
|
-
}, formattedCoordinates));
|
|
31096
|
-
return label;
|
|
31097
|
-
};
|
|
31098
|
-
const formatVertexAriaLabel = newPoint => {
|
|
31099
|
-
const formattedVertexCoordinates = formatCoordinates(newPoint[X], newPoint[Y]);
|
|
31100
|
-
const label = showAngles ? strings.srAngleVertexAtCoordinatesWithAngleMeasure(_extends({}, formattedVertexCoordinates, {
|
|
31101
|
-
angleMeasure: angleMeasure
|
|
31102
|
-
})) : strings.srAngleSideAtCoordinates(_extends({
|
|
31103
|
-
point: 2,
|
|
31104
|
-
side: "vertex"
|
|
31105
|
-
}, formattedVertexCoordinates));
|
|
31106
|
-
return label;
|
|
31107
|
-
};
|
|
31108
|
-
const initialSideAriaLabel = formatSideAriaLabel("initial", coords[2]);
|
|
31109
|
-
const terminalSideAriaLabel = formatSideAriaLabel("terminal", coords[0]);
|
|
31110
|
-
const vertexAriaLabel = formatVertexAriaLabel(coords[1]);
|
|
31086
|
+
srAngleGraphAriaLabel,
|
|
31087
|
+
srAngleGraphAriaDescription,
|
|
31088
|
+
srAngleStartingSide,
|
|
31089
|
+
srAngleEndingSide,
|
|
31090
|
+
srAngleVertex
|
|
31091
|
+
} = describeAngleGraph(graphState, i18n);
|
|
31111
31092
|
|
|
31112
31093
|
// Render the lines, angle, and then movable points
|
|
31113
31094
|
return /*#__PURE__*/React.createElement("g", {
|
|
31114
|
-
"aria-label":
|
|
31115
|
-
"aria-describedby":
|
|
31095
|
+
"aria-label": srAngleGraphAriaLabel,
|
|
31096
|
+
"aria-describedby": descriptionId
|
|
31116
31097
|
}, svgLines, /*#__PURE__*/React.createElement(Angle, angleParams), /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
31117
31098
|
point: coords[1],
|
|
31118
31099
|
sequenceNumber: 1,
|
|
31119
31100
|
constrain: p => p,
|
|
31120
31101
|
onMove: destination => dispatch(actions.angle.movePoint(1, destination)),
|
|
31121
|
-
ariaLabel:
|
|
31102
|
+
ariaLabel: srAngleVertex
|
|
31122
31103
|
}), /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
31123
31104
|
point: coords[0],
|
|
31124
31105
|
sequenceNumber: 2,
|
|
31125
31106
|
constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
|
|
31126
31107
|
onMove: destination => dispatch(actions.angle.movePoint(0, destination)),
|
|
31127
|
-
ariaLabel:
|
|
31108
|
+
ariaLabel: srAngleEndingSide
|
|
31128
31109
|
}), /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
31129
31110
|
point: coords[2],
|
|
31130
31111
|
sequenceNumber: 3,
|
|
31131
31112
|
constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
|
|
31132
31113
|
onMove: destination => dispatch(actions.angle.movePoint(2, destination)),
|
|
31133
|
-
ariaLabel:
|
|
31114
|
+
ariaLabel: srAngleStartingSide
|
|
31134
31115
|
}), /*#__PURE__*/React.createElement("g", {
|
|
31135
|
-
id:
|
|
31116
|
+
id: descriptionId,
|
|
31136
31117
|
style: a11y.srOnly
|
|
31137
|
-
},
|
|
31118
|
+
}, srAngleGraphAriaDescription));
|
|
31119
|
+
}
|
|
31120
|
+
function getAngleGraphDescription(state, i18n) {
|
|
31121
|
+
const {
|
|
31122
|
+
strings,
|
|
31123
|
+
locale
|
|
31124
|
+
} = i18n;
|
|
31125
|
+
const {
|
|
31126
|
+
coords
|
|
31127
|
+
} = state;
|
|
31128
|
+
return strings.srInteractiveElements({
|
|
31129
|
+
elements: strings.srAngleInteractiveElements({
|
|
31130
|
+
vertexX: srFormatNumber(coords[1][X], locale),
|
|
31131
|
+
vertexY: srFormatNumber(coords[1][Y], locale),
|
|
31132
|
+
startingSideX: srFormatNumber(coords[2][X], locale),
|
|
31133
|
+
startingSideY: srFormatNumber(coords[2][Y], locale),
|
|
31134
|
+
endingSideX: srFormatNumber(coords[0][X], locale),
|
|
31135
|
+
endingSideY: srFormatNumber(coords[0][Y], locale)
|
|
31136
|
+
})
|
|
31137
|
+
});
|
|
31138
|
+
}
|
|
31139
|
+
function describeAngleGraph(state, i18n) {
|
|
31140
|
+
const {
|
|
31141
|
+
strings,
|
|
31142
|
+
locale
|
|
31143
|
+
} = i18n;
|
|
31144
|
+
const {
|
|
31145
|
+
coords,
|
|
31146
|
+
allowReflexAngles,
|
|
31147
|
+
showAngles
|
|
31148
|
+
} = state;
|
|
31149
|
+
const [endingSide, vertex, startingSide] = coords;
|
|
31150
|
+
const angleMeasure = srFormatNumber(getClockwiseAngle$2(coords, allowReflexAngles), locale);
|
|
31151
|
+
const srAngleGraphAriaLabel = strings.srAngleGraphAriaLabel;
|
|
31152
|
+
const srAngleGraphAriaDescription = strings.srAngleGraphAriaDescription({
|
|
31153
|
+
angleMeasure,
|
|
31154
|
+
vertexX: srFormatNumber(vertex[X], locale),
|
|
31155
|
+
vertexY: srFormatNumber(vertex[Y], locale),
|
|
31156
|
+
startingSideX: srFormatNumber(startingSide[X], locale),
|
|
31157
|
+
startingSideY: srFormatNumber(startingSide[Y], locale),
|
|
31158
|
+
endingSideX: srFormatNumber(endingSide[X], locale),
|
|
31159
|
+
endingSideY: srFormatNumber(endingSide[Y], locale)
|
|
31160
|
+
});
|
|
31161
|
+
const srAngleStartingSide = strings.srAngleStartingSide({
|
|
31162
|
+
x: srFormatNumber(startingSide[X], locale),
|
|
31163
|
+
y: srFormatNumber(startingSide[Y], locale)
|
|
31164
|
+
});
|
|
31165
|
+
const srAngleEndingSide = strings.srAngleEndingSide({
|
|
31166
|
+
x: srFormatNumber(endingSide[X], locale),
|
|
31167
|
+
y: srFormatNumber(endingSide[Y], locale)
|
|
31168
|
+
});
|
|
31169
|
+
const srAngleVertex = showAngles ? strings.srAngleVertexWithAngleMeasure({
|
|
31170
|
+
x: srFormatNumber(vertex[X], locale),
|
|
31171
|
+
y: srFormatNumber(vertex[Y], locale),
|
|
31172
|
+
angleMeasure
|
|
31173
|
+
}) : strings.srAngleVertex({
|
|
31174
|
+
x: srFormatNumber(vertex[X], locale),
|
|
31175
|
+
y: srFormatNumber(vertex[Y], locale)
|
|
31176
|
+
});
|
|
31177
|
+
return {
|
|
31178
|
+
srAngleGraphAriaLabel,
|
|
31179
|
+
srAngleGraphAriaDescription,
|
|
31180
|
+
srAngleStartingSide,
|
|
31181
|
+
srAngleEndingSide,
|
|
31182
|
+
srAngleVertex
|
|
31183
|
+
};
|
|
31138
31184
|
}
|
|
31139
31185
|
const positiveX = [1, 0];
|
|
31140
31186
|
const negativeX = [-1, 0];
|
|
@@ -31177,9 +31223,6 @@ function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
|
|
|
31177
31223
|
};
|
|
31178
31224
|
}
|
|
31179
31225
|
|
|
31180
|
-
const {
|
|
31181
|
-
clockwise
|
|
31182
|
-
} = geometry;
|
|
31183
31226
|
function getGradableGraph(state, initialGraph) {
|
|
31184
31227
|
if (!state.hasBeenInteractedWith) {
|
|
31185
31228
|
return _extends({}, initialGraph);
|
|
@@ -31243,16 +31286,8 @@ function getGradableGraph(state, initialGraph) {
|
|
|
31243
31286
|
});
|
|
31244
31287
|
}
|
|
31245
31288
|
if (state.type === "angle" && initialGraph.type === "angle") {
|
|
31246
|
-
// We're going to reverse the coords for scoring if the angle is clockwise and
|
|
31247
|
-
// we don't allow reflex angles. This is because the angle is largely defined
|
|
31248
|
-
// by the order of the points in the coords array, and we want to maintain
|
|
31249
|
-
// the same angle scoring with the legacy graph (which had a bug).
|
|
31250
|
-
// (LEMS-2190): When we remove the legacy graph, move this logic to the scoring function.
|
|
31251
|
-
const areClockwise = clockwise([state.coords[0], state.coords[2], state.coords[1]]);
|
|
31252
|
-
const shouldReverseCoords = areClockwise && !state.allowReflexAngles;
|
|
31253
|
-
const coords = shouldReverseCoords ? state.coords.slice().reverse() : state.coords;
|
|
31254
31289
|
return _extends({}, initialGraph, {
|
|
31255
|
-
coords,
|
|
31290
|
+
coords: state.coords,
|
|
31256
31291
|
allowReflexAngles: state.allowReflexAngles
|
|
31257
31292
|
});
|
|
31258
31293
|
}
|
|
@@ -31276,17 +31311,16 @@ function getRadius(graph) {
|
|
|
31276
31311
|
return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
|
|
31277
31312
|
}
|
|
31278
31313
|
|
|
31279
|
-
function renderCircleGraph(state, dispatch) {
|
|
31314
|
+
function renderCircleGraph(state, dispatch, i18n) {
|
|
31280
31315
|
return {
|
|
31281
31316
|
graph: /*#__PURE__*/React.createElement(CircleGraph, {
|
|
31282
31317
|
graphState: state,
|
|
31283
31318
|
dispatch: dispatch
|
|
31284
31319
|
}),
|
|
31285
|
-
interactiveElementsDescription:
|
|
31286
|
-
state: state
|
|
31287
|
-
})
|
|
31320
|
+
interactiveElementsDescription: getCircleGraphDescription(state, i18n)
|
|
31288
31321
|
};
|
|
31289
31322
|
}
|
|
31323
|
+
// Exported for testing
|
|
31290
31324
|
function CircleGraph(props) {
|
|
31291
31325
|
const {
|
|
31292
31326
|
dispatch,
|
|
@@ -31379,6 +31413,7 @@ function MovableCircle(props) {
|
|
|
31379
31413
|
snapStep,
|
|
31380
31414
|
disableKeyboardInteraction
|
|
31381
31415
|
} = useGraphConfig();
|
|
31416
|
+
const [focused, setFocused] = React.useState(false);
|
|
31382
31417
|
const draggableRef = useRef(null);
|
|
31383
31418
|
const {
|
|
31384
31419
|
dragging
|
|
@@ -31397,7 +31432,9 @@ function MovableCircle(props) {
|
|
|
31397
31432
|
ref: draggableRef,
|
|
31398
31433
|
role: "button",
|
|
31399
31434
|
tabIndex: disableKeyboardInteraction ? -1 : 0,
|
|
31400
|
-
className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}
|
|
31435
|
+
className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`,
|
|
31436
|
+
onFocus: () => setFocused(true),
|
|
31437
|
+
onBlur: () => setFocused(false)
|
|
31401
31438
|
}, /*#__PURE__*/React.createElement("ellipse", {
|
|
31402
31439
|
className: "focus-ring",
|
|
31403
31440
|
cx: centerPx[X],
|
|
@@ -31412,19 +31449,29 @@ function MovableCircle(props) {
|
|
|
31412
31449
|
rx: radiiPx[X],
|
|
31413
31450
|
ry: radiiPx[Y]
|
|
31414
31451
|
}), /*#__PURE__*/React.createElement(DragHandle, {
|
|
31415
|
-
center: center
|
|
31452
|
+
center: center,
|
|
31453
|
+
dragging: dragging,
|
|
31454
|
+
focused: focused
|
|
31416
31455
|
}));
|
|
31417
31456
|
}
|
|
31418
31457
|
const dragHandleDimensions = [24, 14];
|
|
31419
31458
|
const dragHandleDotPositions = crossProduct([-4.4, 0, 4.4], [-2.1, 2.1]);
|
|
31420
31459
|
function DragHandle(props) {
|
|
31421
31460
|
const {
|
|
31422
|
-
center
|
|
31461
|
+
center,
|
|
31462
|
+
dragging,
|
|
31463
|
+
focused
|
|
31423
31464
|
} = props;
|
|
31424
|
-
const cornerRadius = Math.min(...dragHandleDimensions) / 2;
|
|
31425
31465
|
const [centerPx] = useTransformVectorsToPixels(center);
|
|
31466
|
+
const {
|
|
31467
|
+
markings
|
|
31468
|
+
} = useGraphConfig();
|
|
31469
|
+
const cornerRadius = Math.min(...dragHandleDimensions) / 2;
|
|
31426
31470
|
const topLeft = vec.sub(centerPx, vec.scale(dragHandleDimensions, 0.5));
|
|
31427
|
-
|
|
31471
|
+
const showHairlines = (dragging || focused) && markings !== "none";
|
|
31472
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines && /*#__PURE__*/React.createElement(Hairlines, {
|
|
31473
|
+
point: center
|
|
31474
|
+
}), /*#__PURE__*/React.createElement("rect", {
|
|
31428
31475
|
className: "movable-circle-handle",
|
|
31429
31476
|
x: topLeft[X],
|
|
31430
31477
|
y: topLeft[Y],
|
|
@@ -31451,13 +31498,7 @@ function crossProduct(as, bs) {
|
|
|
31451
31498
|
}
|
|
31452
31499
|
return result;
|
|
31453
31500
|
}
|
|
31454
|
-
function
|
|
31455
|
-
state
|
|
31456
|
-
}) {
|
|
31457
|
-
// The reason that CircleGraphDescription is a component (rather than a
|
|
31458
|
-
// function that returns a string) is because it needs to use a
|
|
31459
|
-
// hook: `usePerseusI18n`.
|
|
31460
|
-
const i18n = usePerseusI18n();
|
|
31501
|
+
function getCircleGraphDescription(state, i18n) {
|
|
31461
31502
|
const strings = describeCircleGraph(state, i18n);
|
|
31462
31503
|
return strings.srCircleInteractiveElement;
|
|
31463
31504
|
}
|
|
@@ -31510,15 +31551,13 @@ function describeCircleGraph(state, i18n) {
|
|
|
31510
31551
|
};
|
|
31511
31552
|
}
|
|
31512
31553
|
|
|
31513
|
-
function renderLinearGraph(state, dispatch) {
|
|
31554
|
+
function renderLinearGraph(state, dispatch, i18n) {
|
|
31514
31555
|
return {
|
|
31515
31556
|
graph: /*#__PURE__*/React.createElement(LinearGraph, {
|
|
31516
31557
|
graphState: state,
|
|
31517
31558
|
dispatch: dispatch
|
|
31518
31559
|
}),
|
|
31519
|
-
interactiveElementsDescription:
|
|
31520
|
-
state: state
|
|
31521
|
-
})
|
|
31560
|
+
interactiveElementsDescription: getLinearGraphDescription(state, i18n)
|
|
31522
31561
|
};
|
|
31523
31562
|
}
|
|
31524
31563
|
const LinearGraph = (props, key) => {
|
|
@@ -31583,13 +31622,7 @@ const LinearGraph = (props, key) => {
|
|
|
31583
31622
|
style: a11y.srOnly
|
|
31584
31623
|
}, slopeString));
|
|
31585
31624
|
};
|
|
31586
|
-
function
|
|
31587
|
-
state
|
|
31588
|
-
}) {
|
|
31589
|
-
// The reason that LinearGraphDescription is a component (rather than a
|
|
31590
|
-
// function that returns a string) is because it needs to use a
|
|
31591
|
-
// hook: `usePerseusI18n`.
|
|
31592
|
-
const i18n = usePerseusI18n();
|
|
31625
|
+
function getLinearGraphDescription(state, i18n) {
|
|
31593
31626
|
const strings = describeLinearGraph(state, i18n);
|
|
31594
31627
|
return strings.srLinearInteractiveElement;
|
|
31595
31628
|
}
|
|
@@ -31633,15 +31666,13 @@ function describeLinearGraph(state, i18n) {
|
|
|
31633
31666
|
};
|
|
31634
31667
|
}
|
|
31635
31668
|
|
|
31636
|
-
function renderLinearSystemGraph(state, dispatch) {
|
|
31669
|
+
function renderLinearSystemGraph(state, dispatch, i18n) {
|
|
31637
31670
|
return {
|
|
31638
31671
|
graph: /*#__PURE__*/React.createElement(LinearSystemGraph, {
|
|
31639
31672
|
graphState: state,
|
|
31640
31673
|
dispatch: dispatch
|
|
31641
31674
|
}),
|
|
31642
|
-
interactiveElementsDescription:
|
|
31643
|
-
state: state
|
|
31644
|
-
})
|
|
31675
|
+
interactiveElementsDescription: getLinearSystemGraphDescription(state, i18n)
|
|
31645
31676
|
};
|
|
31646
31677
|
}
|
|
31647
31678
|
const LinearSystemGraph = props => {
|
|
@@ -31728,18 +31759,9 @@ const LinearSystemGraph = props => {
|
|
|
31728
31759
|
style: a11y.srOnly
|
|
31729
31760
|
}, slopeDescription))));
|
|
31730
31761
|
};
|
|
31731
|
-
function LinearSystemGraphDescription({
|
|
31732
|
-
state
|
|
31733
|
-
}) {
|
|
31734
|
-
// The reason that LinearSystemGraphDescription is a component (rather
|
|
31735
|
-
// than a function that returns a string) is because it needs to use a
|
|
31736
|
-
// hook: `usePerseusI18n`.
|
|
31737
|
-
const i18n = usePerseusI18n();
|
|
31738
|
-
return describeLinearSystemGraph(state, i18n);
|
|
31739
|
-
}
|
|
31740
31762
|
|
|
31741
31763
|
// Exported for testing
|
|
31742
|
-
function
|
|
31764
|
+
function getLinearSystemGraphDescription(state, i18n) {
|
|
31743
31765
|
const {
|
|
31744
31766
|
strings,
|
|
31745
31767
|
locale
|
|
@@ -31765,15 +31787,13 @@ function describeLinearSystemGraph(state, i18n) {
|
|
|
31765
31787
|
});
|
|
31766
31788
|
}
|
|
31767
31789
|
|
|
31768
|
-
function renderPointGraph(state, dispatch) {
|
|
31790
|
+
function renderPointGraph(state, dispatch, i18n) {
|
|
31769
31791
|
return {
|
|
31770
31792
|
graph: /*#__PURE__*/React.createElement(PointGraph, {
|
|
31771
31793
|
graphState: state,
|
|
31772
31794
|
dispatch: dispatch
|
|
31773
31795
|
}),
|
|
31774
|
-
interactiveElementsDescription:
|
|
31775
|
-
state: state
|
|
31776
|
-
})
|
|
31796
|
+
interactiveElementsDescription: getPointGraphDescription(state, i18n)
|
|
31777
31797
|
};
|
|
31778
31798
|
}
|
|
31779
31799
|
function PointGraph(props) {
|
|
@@ -31796,7 +31816,9 @@ function PointGraph(props) {
|
|
|
31796
31816
|
var _pointsRef$current$fo;
|
|
31797
31817
|
(_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
|
|
31798
31818
|
}
|
|
31799
|
-
}, [props.graphState.focusedPointIndex,
|
|
31819
|
+
}, [props.graphState.focusedPointIndex,
|
|
31820
|
+
// Ensure that we re-focus if a point is deleted.
|
|
31821
|
+
props.graphState.coords.length, pointsRef]);
|
|
31800
31822
|
const statefulProps = _extends({}, props, {
|
|
31801
31823
|
graphConfig,
|
|
31802
31824
|
pointsRef,
|
|
@@ -31867,17 +31889,9 @@ function UnlimitedPointGraph(statefulProps) {
|
|
|
31867
31889
|
}
|
|
31868
31890
|
})));
|
|
31869
31891
|
}
|
|
31870
|
-
function PointGraphDescription({
|
|
31871
|
-
state
|
|
31872
|
-
}) {
|
|
31873
|
-
// PointGraphDescription needs to `usePerseusI18n`, so it has to be a
|
|
31874
|
-
// component rather than a function that simply returns a string.
|
|
31875
|
-
const i18n = usePerseusI18n();
|
|
31876
|
-
return describePointGraph(state, i18n);
|
|
31877
|
-
}
|
|
31878
31892
|
|
|
31879
31893
|
// Exported for testing
|
|
31880
|
-
function
|
|
31894
|
+
function getPointGraphDescription(state, i18n) {
|
|
31881
31895
|
const {
|
|
31882
31896
|
strings,
|
|
31883
31897
|
locale
|
|
@@ -31891,17 +31905,20 @@ function describePointGraph(state, i18n) {
|
|
|
31891
31905
|
y: srFormatNumber(y, locale)
|
|
31892
31906
|
}));
|
|
31893
31907
|
return strings.srInteractiveElements({
|
|
31894
|
-
elements: pointDescriptions.join("
|
|
31908
|
+
elements: pointDescriptions.join(" ")
|
|
31895
31909
|
});
|
|
31896
31910
|
}
|
|
31897
31911
|
|
|
31898
|
-
|
|
31912
|
+
const {
|
|
31913
|
+
convertRadiansToDegrees
|
|
31914
|
+
} = angles;
|
|
31915
|
+
function renderPolygonGraph(state, dispatch, i18n, markings) {
|
|
31899
31916
|
return {
|
|
31900
31917
|
graph: /*#__PURE__*/React.createElement(PolygonGraph, {
|
|
31901
31918
|
graphState: state,
|
|
31902
31919
|
dispatch: dispatch
|
|
31903
31920
|
}),
|
|
31904
|
-
interactiveElementsDescription:
|
|
31921
|
+
interactiveElementsDescription: getPolygonGraphDescription(state, i18n, markings)
|
|
31905
31922
|
};
|
|
31906
31923
|
}
|
|
31907
31924
|
const PolygonGraph = props => {
|
|
@@ -31961,7 +31978,9 @@ const PolygonGraph = props => {
|
|
|
31961
31978
|
var _pointsRef$current$fo;
|
|
31962
31979
|
(_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
|
|
31963
31980
|
}
|
|
31964
|
-
}, [props.graphState.focusedPointIndex,
|
|
31981
|
+
}, [props.graphState.focusedPointIndex,
|
|
31982
|
+
// Ensure that we re-focus if a point is deleted.
|
|
31983
|
+
props.graphState.coords.length, pointsRef]);
|
|
31965
31984
|
|
|
31966
31985
|
// If the unlimited polygon is rendered with 3 or more coordinates
|
|
31967
31986
|
// Close the polygon, but only on first render.
|
|
@@ -31986,7 +32005,7 @@ const PolygonGraph = props => {
|
|
|
31986
32005
|
focusVisible,
|
|
31987
32006
|
setFocusVisible
|
|
31988
32007
|
});
|
|
31989
|
-
return numSides === "unlimited" ? UnlimitedPolygonGraph
|
|
32008
|
+
return numSides === "unlimited" ? /*#__PURE__*/React.createElement(UnlimitedPolygonGraph, statefulProps) : /*#__PURE__*/React.createElement(LimitedPolygonGraph, statefulProps);
|
|
31990
32009
|
};
|
|
31991
32010
|
const LimitedPolygonGraph = statefulProps => {
|
|
31992
32011
|
const {
|
|
@@ -32011,8 +32030,39 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32011
32030
|
const {
|
|
32012
32031
|
disableKeyboardInteraction
|
|
32013
32032
|
} = graphConfig;
|
|
32033
|
+
const {
|
|
32034
|
+
strings,
|
|
32035
|
+
locale
|
|
32036
|
+
} = usePerseusI18n();
|
|
32037
|
+
const id = React.useId();
|
|
32038
|
+
const pointsOffArray = Array(points.length).fill("off");
|
|
32039
|
+
// When moving an element, set its aria-live to "polite" and the others
|
|
32040
|
+
// to "off". Otherwise, other connected elements that move at the same
|
|
32041
|
+
// time might override the currently focused element's aria live.
|
|
32042
|
+
const [ariaLives, setAriaLives] = React.useState([
|
|
32043
|
+
// First one represents the aria-live value for the polygon itself.
|
|
32044
|
+
"off",
|
|
32045
|
+
// The rest represent the points.
|
|
32046
|
+
...pointsOffArray]);
|
|
32014
32047
|
const lines = getLines(points);
|
|
32015
|
-
|
|
32048
|
+
const polygonPointsNumId = id + "-points-num";
|
|
32049
|
+
const polygonPointsId = id + "-points";
|
|
32050
|
+
|
|
32051
|
+
// Aria label srings
|
|
32052
|
+
const {
|
|
32053
|
+
srPolygonGraph,
|
|
32054
|
+
srPolygonGraphPointsNum,
|
|
32055
|
+
srPolygonGraphPoints,
|
|
32056
|
+
srPolygonElementsNum
|
|
32057
|
+
} = describePolygonGraph(statefulProps.graphState, {
|
|
32058
|
+
strings,
|
|
32059
|
+
locale
|
|
32060
|
+
}, statefulProps.graphConfig.markings);
|
|
32061
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
32062
|
+
// Outer graph minimal description
|
|
32063
|
+
"aria-label": srPolygonGraph,
|
|
32064
|
+
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
32065
|
+
}, /*#__PURE__*/React.createElement(Polygon, {
|
|
32016
32066
|
points: [...points],
|
|
32017
32067
|
color: "var(--movable-line-stroke-color)",
|
|
32018
32068
|
svgPolygonProps: {
|
|
@@ -32037,13 +32087,17 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32037
32087
|
snapTo: snapTo
|
|
32038
32088
|
});
|
|
32039
32089
|
}), showSides && lines.map(([start, end], i) => {
|
|
32090
|
+
// Use x and y to find the position of the label
|
|
32040
32091
|
const [x, y] = vec.midpoint(start, end);
|
|
32041
|
-
const length =
|
|
32092
|
+
const length = vec.dist(start, end);
|
|
32093
|
+
// Check if the length needs to indicate
|
|
32094
|
+
// that it's an approximation.
|
|
32095
|
+
const isApprox = !Number.isInteger(length);
|
|
32042
32096
|
return /*#__PURE__*/React.createElement(TextLabel, {
|
|
32043
32097
|
key: "side-" + i,
|
|
32044
32098
|
x: x,
|
|
32045
32099
|
y: y
|
|
32046
|
-
},
|
|
32100
|
+
}, isApprox ? `≈ ${length.toFixed(snapTo === "sides" ? 0 : 1)}` : length);
|
|
32047
32101
|
}), /*#__PURE__*/React.createElement(Polygon, {
|
|
32048
32102
|
points: [...points],
|
|
32049
32103
|
color: "transparent",
|
|
@@ -32063,27 +32117,80 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32063
32117
|
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
32064
32118
|
},
|
|
32065
32119
|
// Required for lines to darken on focus
|
|
32066
|
-
onFocus: () =>
|
|
32120
|
+
onFocus: () => {
|
|
32121
|
+
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
32122
|
+
setAriaLives(() => ["polite", ...pointsOffArray]);
|
|
32123
|
+
},
|
|
32067
32124
|
// Required for line weighting to update on blur. Without this,
|
|
32068
32125
|
// the user has to hover over the shape for it to update
|
|
32069
32126
|
onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
32070
|
-
className: "movable-polygon"
|
|
32071
|
-
|
|
32072
|
-
|
|
32073
|
-
|
|
32074
|
-
|
|
32075
|
-
point: point,
|
|
32076
|
-
sequenceNumber: i + 1,
|
|
32077
|
-
onMove: destination => {
|
|
32078
|
-
const now = Date.now();
|
|
32079
|
-
const targetFPS = 40;
|
|
32080
|
-
const moveThresholdTime = 1000 / targetFPS;
|
|
32081
|
-
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
32082
|
-
dispatch(actions.polygon.movePoint(i, destination));
|
|
32083
|
-
lastMoveTimeRef.current = now;
|
|
32084
|
-
}
|
|
32127
|
+
className: "movable-polygon",
|
|
32128
|
+
// Accessibility-related fields
|
|
32129
|
+
role: "button",
|
|
32130
|
+
"aria-label": srPolygonGraphPoints ? `${srPolygonElementsNum} ${srPolygonGraphPoints}` : srPolygonElementsNum,
|
|
32131
|
+
"aria-live": ariaLives[0]
|
|
32085
32132
|
}
|
|
32086
|
-
}))
|
|
32133
|
+
}), points.map((point, i) => {
|
|
32134
|
+
const angleId = `${id}-angle-${i}`;
|
|
32135
|
+
const side1Id = `${id}-point-${i}-side-1`;
|
|
32136
|
+
const side2Id = `${id}-point-${i}-side-2`;
|
|
32137
|
+
|
|
32138
|
+
// Limited polygons always have an angle at each vertex.
|
|
32139
|
+
const angle = getAngleFromPoints(points, i);
|
|
32140
|
+
const angleDegree = angle ? convertRadiansToDegrees(angle) : null;
|
|
32141
|
+
|
|
32142
|
+
// Limited polygons always two sides attached to each vertex.
|
|
32143
|
+
const sidesArray = getSideLengthsFromPoints(points, i);
|
|
32144
|
+
const {
|
|
32145
|
+
pointIndex: point1Index,
|
|
32146
|
+
sideLength: side1Length
|
|
32147
|
+
} = sidesArray[0];
|
|
32148
|
+
const {
|
|
32149
|
+
pointIndex: point2Index,
|
|
32150
|
+
sideLength: side2Length
|
|
32151
|
+
} = sidesArray[1];
|
|
32152
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
32153
|
+
key: "point-" + i
|
|
32154
|
+
}, /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
32155
|
+
ariaDescribedBy: `${angleId} ${side1Id} ${side2Id}`,
|
|
32156
|
+
ariaLive: ariaLives[i + 1],
|
|
32157
|
+
constrain: constrain,
|
|
32158
|
+
point: point,
|
|
32159
|
+
sequenceNumber: i + 1,
|
|
32160
|
+
onMove: destination => {
|
|
32161
|
+
const now = Date.now();
|
|
32162
|
+
const targetFPS = 40;
|
|
32163
|
+
const moveThresholdTime = 1000 / targetFPS;
|
|
32164
|
+
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
32165
|
+
dispatch(actions.polygon.movePoint(i, destination));
|
|
32166
|
+
lastMoveTimeRef.current = now;
|
|
32167
|
+
}
|
|
32168
|
+
},
|
|
32169
|
+
onFocus: () => {
|
|
32170
|
+
const newPointAriaLives = [...pointsOffArray];
|
|
32171
|
+
newPointAriaLives[i] = "polite";
|
|
32172
|
+
// Whole polygon is "off", and the current
|
|
32173
|
+
// point is "polite".
|
|
32174
|
+
setAriaLives(["off", ...newPointAriaLives]);
|
|
32175
|
+
}
|
|
32176
|
+
}), angleDegree && /*#__PURE__*/React.createElement("g", {
|
|
32177
|
+
id: angleId
|
|
32178
|
+
}, Number.isInteger(angleDegree) ? strings.srPolygonPointAngle({
|
|
32179
|
+
angle: angleDegree
|
|
32180
|
+
}) : strings.srPolygonPointAngleApprox({
|
|
32181
|
+
angle: srFormatNumber(angleDegree, locale, 1)
|
|
32182
|
+
})), /*#__PURE__*/React.createElement("g", {
|
|
32183
|
+
id: side1Id
|
|
32184
|
+
}, getPolygonSideString(side1Length, point1Index, strings, locale)), /*#__PURE__*/React.createElement("g", {
|
|
32185
|
+
id: side2Id
|
|
32186
|
+
}, getPolygonSideString(side2Length, point2Index, strings, locale)));
|
|
32187
|
+
}), /*#__PURE__*/React.createElement("g", {
|
|
32188
|
+
id: polygonPointsNumId,
|
|
32189
|
+
style: a11y.srOnly
|
|
32190
|
+
}, srPolygonGraphPointsNum), srPolygonGraphPoints && /*#__PURE__*/React.createElement("g", {
|
|
32191
|
+
id: polygonPointsId,
|
|
32192
|
+
style: a11y.srOnly
|
|
32193
|
+
}, srPolygonGraphPoints));
|
|
32087
32194
|
};
|
|
32088
32195
|
const UnlimitedPolygonGraph = statefulProps => {
|
|
32089
32196
|
const {
|
|
@@ -32098,6 +32205,19 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32098
32205
|
coords,
|
|
32099
32206
|
closedPolygon
|
|
32100
32207
|
} = statefulProps.graphState;
|
|
32208
|
+
const {
|
|
32209
|
+
strings,
|
|
32210
|
+
locale
|
|
32211
|
+
} = usePerseusI18n();
|
|
32212
|
+
const id = React.useId();
|
|
32213
|
+
const polygonPointsNumId = id + "-points-num";
|
|
32214
|
+
const polygonPointsId = id + "-points";
|
|
32215
|
+
|
|
32216
|
+
// When moving an element, set its aria-live to "polite" and the others
|
|
32217
|
+
// to "off". Otherwise, other connected elements that move at the same
|
|
32218
|
+
// time might override the currently focused element's aria live.
|
|
32219
|
+
const pointsOffArray = Array(points.length).fill("off");
|
|
32220
|
+
const [ariaLives, setAriaLives] = React.useState(pointsOffArray);
|
|
32101
32221
|
|
|
32102
32222
|
// If the polygon is closed, return a LimitedPolygon component.
|
|
32103
32223
|
if (closedPolygon) {
|
|
@@ -32111,7 +32231,22 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32111
32231
|
} = graphConfig;
|
|
32112
32232
|
const widthPx = graphDimensionsInPixels[0];
|
|
32113
32233
|
const heightPx = graphDimensionsInPixels[1];
|
|
32114
|
-
|
|
32234
|
+
|
|
32235
|
+
// Aria label srings
|
|
32236
|
+
const emptyGraph = coords.length === 0;
|
|
32237
|
+
const {
|
|
32238
|
+
srPolygonGraph,
|
|
32239
|
+
srPolygonGraphPointsNum,
|
|
32240
|
+
srPolygonGraphPoints
|
|
32241
|
+
} = describePolygonGraph(statefulProps.graphState, {
|
|
32242
|
+
strings,
|
|
32243
|
+
locale
|
|
32244
|
+
}, statefulProps.graphConfig.markings);
|
|
32245
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
32246
|
+
// Outer graph minimal description
|
|
32247
|
+
"aria-label": emptyGraph ? strings.srUnlimitedPolygonEmpty : srPolygonGraph,
|
|
32248
|
+
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
32249
|
+
}, /*#__PURE__*/React.createElement(Polyline, {
|
|
32115
32250
|
points: [...points],
|
|
32116
32251
|
color: "var(--movable-line-stroke-color)",
|
|
32117
32252
|
svgPolylineProps: {
|
|
@@ -32136,28 +32271,68 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32136
32271
|
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
32137
32272
|
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
32138
32273
|
}
|
|
32139
|
-
}), coords.map((point, i) =>
|
|
32140
|
-
|
|
32141
|
-
|
|
32142
|
-
|
|
32143
|
-
|
|
32144
|
-
|
|
32145
|
-
|
|
32146
|
-
|
|
32147
|
-
|
|
32148
|
-
|
|
32149
|
-
|
|
32150
|
-
|
|
32151
|
-
// If the point being clicked is the first point and
|
|
32152
|
-
// there's enough non-duplicated points to form
|
|
32153
|
-
// a polygon (3 or more), close the shape before
|
|
32154
|
-
// setting focus.
|
|
32155
|
-
if (i === 0 && getArrayWithoutDuplicates(coords).length >= 3) {
|
|
32156
|
-
dispatch(actions.polygon.closePolygon());
|
|
32157
|
-
}
|
|
32158
|
-
dispatch(actions.polygon.clickPoint(i));
|
|
32274
|
+
}), coords.map((point, i) => {
|
|
32275
|
+
const angleId = `${id}-angle-${i}`;
|
|
32276
|
+
let sideIds = "";
|
|
32277
|
+
|
|
32278
|
+
// This point only has an angle if it's not the first
|
|
32279
|
+
// or last point in this open polygon.
|
|
32280
|
+
const hasAngle = i > 0 && i < coords.length - 1;
|
|
32281
|
+
const angle = hasAngle ? getAngleFromPoints(points, i) : null;
|
|
32282
|
+
const angleDegree = angle ? convertRadiansToDegrees(angle) : null;
|
|
32283
|
+
const sidesArray = getSideLengthsFromPoints(points, i, true);
|
|
32284
|
+
for (let sideIndex = 0; sideIndex < sidesArray.length; sideIndex++) {
|
|
32285
|
+
sideIds += `${id}-point-${i}-side-${sideIndex} `;
|
|
32159
32286
|
}
|
|
32160
|
-
|
|
32287
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
32288
|
+
key: "point-" + i
|
|
32289
|
+
}, /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
32290
|
+
ariaDescribedBy: `${angleId} ${sideIds}`,
|
|
32291
|
+
ariaLive: ariaLives[i],
|
|
32292
|
+
point: point,
|
|
32293
|
+
sequenceNumber: i + 1,
|
|
32294
|
+
onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
|
|
32295
|
+
ref: ref => {
|
|
32296
|
+
pointsRef.current[i] = ref;
|
|
32297
|
+
},
|
|
32298
|
+
onFocus: () => {
|
|
32299
|
+
dispatch(actions.polygon.focusPoint(i));
|
|
32300
|
+
const newPointAriaLives = [...pointsOffArray];
|
|
32301
|
+
newPointAriaLives[i] = "polite";
|
|
32302
|
+
// Whole polygon is "off", and the current
|
|
32303
|
+
// point is "polite".
|
|
32304
|
+
setAriaLives([...newPointAriaLives]);
|
|
32305
|
+
},
|
|
32306
|
+
onClick: () => {
|
|
32307
|
+
// If the point being clicked is the first point and
|
|
32308
|
+
// there's enough non-duplicated points to form
|
|
32309
|
+
// a polygon (3 or more), close the shape before
|
|
32310
|
+
// setting focus.
|
|
32311
|
+
if (i === 0 && getArrayWithoutDuplicates(coords).length >= 3) {
|
|
32312
|
+
dispatch(actions.polygon.closePolygon());
|
|
32313
|
+
}
|
|
32314
|
+
dispatch(actions.polygon.clickPoint(i));
|
|
32315
|
+
}
|
|
32316
|
+
}), angleDegree && /*#__PURE__*/React.createElement("g", {
|
|
32317
|
+
id: angleId
|
|
32318
|
+
}, Number.isInteger(angleDegree) ? strings.srPolygonPointAngle({
|
|
32319
|
+
angle: angleDegree
|
|
32320
|
+
}) : strings.srPolygonPointAngleApprox({
|
|
32321
|
+
angle: srFormatNumber(angleDegree, locale, 1)
|
|
32322
|
+
})), sidesArray.map(({
|
|
32323
|
+
pointIndex,
|
|
32324
|
+
sideLength
|
|
32325
|
+
}, j) => /*#__PURE__*/React.createElement("g", {
|
|
32326
|
+
key: `${id}-point-${i}-side-${j}`,
|
|
32327
|
+
id: `${id}-point-${i}-side-${j}`
|
|
32328
|
+
}, getPolygonSideString(sideLength, pointIndex, strings, locale))));
|
|
32329
|
+
}), coords.length > 0 && /*#__PURE__*/React.createElement("g", {
|
|
32330
|
+
id: polygonPointsNumId,
|
|
32331
|
+
style: a11y.srOnly
|
|
32332
|
+
}, srPolygonGraphPointsNum), srPolygonGraphPoints && /*#__PURE__*/React.createElement("g", {
|
|
32333
|
+
id: polygonPointsId,
|
|
32334
|
+
style: a11y.srOnly
|
|
32335
|
+
}, srPolygonGraphPoints));
|
|
32161
32336
|
};
|
|
32162
32337
|
function getLines(points) {
|
|
32163
32338
|
return points.map((point, i) => {
|
|
@@ -32179,6 +32354,54 @@ const hasFocusVisible = element => {
|
|
|
32179
32354
|
return matches(":focus");
|
|
32180
32355
|
}
|
|
32181
32356
|
};
|
|
32357
|
+
function getPolygonGraphDescription(state, i18n, markings) {
|
|
32358
|
+
const strings = describePolygonGraph(state, i18n, markings);
|
|
32359
|
+
return strings.srPolygonInteractiveElements;
|
|
32360
|
+
}
|
|
32361
|
+
function describePolygonGraph(state, i18n, markings) {
|
|
32362
|
+
const {
|
|
32363
|
+
strings,
|
|
32364
|
+
locale
|
|
32365
|
+
} = i18n;
|
|
32366
|
+
const {
|
|
32367
|
+
coords
|
|
32368
|
+
} = state;
|
|
32369
|
+
const isCoordinatePlane = markings === "axes" || markings === "graph";
|
|
32370
|
+
const hasOnePoint = coords.length === 1;
|
|
32371
|
+
|
|
32372
|
+
// Figure out graph aria label based on markings.
|
|
32373
|
+
const srPolygonGraph = isCoordinatePlane ? strings.srPolygonGraphCoordinatePlane : strings.srPolygonGraph;
|
|
32374
|
+
const srPolygonGraphPointsNum = hasOnePoint ? strings.srPolygonGraphPointsOne : strings.srPolygonGraphPointsNum({
|
|
32375
|
+
num: coords.length
|
|
32376
|
+
});
|
|
32377
|
+
let srPolygonGraphPoints;
|
|
32378
|
+
// Figure out graph description based on markings.
|
|
32379
|
+
// If the graph is not on a coordinate plane, we should not include
|
|
32380
|
+
// the points' coordinates in the description.
|
|
32381
|
+
if (isCoordinatePlane) {
|
|
32382
|
+
const pointsString = coords.map((coord, i) => {
|
|
32383
|
+
return strings.srPointAtCoordinates({
|
|
32384
|
+
num: i + 1,
|
|
32385
|
+
x: srFormatNumber(coord[0], locale),
|
|
32386
|
+
y: srFormatNumber(coord[1], locale)
|
|
32387
|
+
});
|
|
32388
|
+
});
|
|
32389
|
+
srPolygonGraphPoints = pointsString.join(" ");
|
|
32390
|
+
}
|
|
32391
|
+
const srPolygonElementsNum = hasOnePoint ? strings.srPolygonElementsOne : strings.srPolygonElementsNum({
|
|
32392
|
+
num: coords.length
|
|
32393
|
+
});
|
|
32394
|
+
const srPolygonInteractiveElements = coords.length > 0 ? strings.srInteractiveElements({
|
|
32395
|
+
elements: [srPolygonElementsNum, srPolygonGraphPoints].join(" ")
|
|
32396
|
+
}) : null;
|
|
32397
|
+
return {
|
|
32398
|
+
srPolygonGraph,
|
|
32399
|
+
srPolygonGraphPointsNum,
|
|
32400
|
+
srPolygonGraphPoints,
|
|
32401
|
+
srPolygonElementsNum,
|
|
32402
|
+
srPolygonInteractiveElements
|
|
32403
|
+
};
|
|
32404
|
+
}
|
|
32182
32405
|
|
|
32183
32406
|
function renderQuadraticGraph(state, dispatch, i18n) {
|
|
32184
32407
|
return {
|
|
@@ -32337,15 +32560,13 @@ function describeQuadraticGraph(state, i18n) {
|
|
|
32337
32560
|
};
|
|
32338
32561
|
}
|
|
32339
32562
|
|
|
32340
|
-
function renderRayGraph(state, dispatch) {
|
|
32563
|
+
function renderRayGraph(state, dispatch, i18n) {
|
|
32341
32564
|
return {
|
|
32342
32565
|
graph: /*#__PURE__*/React.createElement(RayGraph, {
|
|
32343
32566
|
graphState: state,
|
|
32344
32567
|
dispatch: dispatch
|
|
32345
32568
|
}),
|
|
32346
|
-
interactiveElementsDescription:
|
|
32347
|
-
state: state
|
|
32348
|
-
})
|
|
32569
|
+
interactiveElementsDescription: getRayGraphDescription(state, i18n)
|
|
32349
32570
|
};
|
|
32350
32571
|
}
|
|
32351
32572
|
const RayGraph = props => {
|
|
@@ -32399,13 +32620,7 @@ const RayGraph = props => {
|
|
|
32399
32620
|
style: a11y.srOnly
|
|
32400
32621
|
}, srRayPoints));
|
|
32401
32622
|
};
|
|
32402
|
-
function
|
|
32403
|
-
state
|
|
32404
|
-
}) {
|
|
32405
|
-
// The reason that RayGraphDescription is a component (rather than a
|
|
32406
|
-
// function that returns a string) is because it needs to use a
|
|
32407
|
-
// hook: `usePerseusI18n`.
|
|
32408
|
-
const i18n = usePerseusI18n();
|
|
32623
|
+
function getRayGraphDescription(state, i18n) {
|
|
32409
32624
|
const strings = describeRayGraph(state, i18n);
|
|
32410
32625
|
return strings.srRayInteractiveElement;
|
|
32411
32626
|
}
|
|
@@ -32455,15 +32670,13 @@ function describeRayGraph(state, i18n) {
|
|
|
32455
32670
|
};
|
|
32456
32671
|
}
|
|
32457
32672
|
|
|
32458
|
-
function renderSegmentGraph(state, dispatch) {
|
|
32673
|
+
function renderSegmentGraph(state, dispatch, i18n) {
|
|
32459
32674
|
return {
|
|
32460
32675
|
graph: /*#__PURE__*/React.createElement(SegmentGraph, {
|
|
32461
32676
|
graphState: state,
|
|
32462
32677
|
dispatch: dispatch
|
|
32463
32678
|
}),
|
|
32464
|
-
interactiveElementsDescription:
|
|
32465
|
-
state: state
|
|
32466
|
-
})
|
|
32679
|
+
interactiveElementsDescription: getSegmentGraphDescription(state, i18n)
|
|
32467
32680
|
};
|
|
32468
32681
|
}
|
|
32469
32682
|
const SegmentGraph = ({
|
|
@@ -32555,18 +32768,9 @@ const SegmentGraph = ({
|
|
|
32555
32768
|
function getLengthOfSegment(segment) {
|
|
32556
32769
|
return point.distanceToPoint(...segment);
|
|
32557
32770
|
}
|
|
32558
|
-
function SegmentGraphDescription({
|
|
32559
|
-
state
|
|
32560
|
-
}) {
|
|
32561
|
-
// The reason that SegmentGraphDescription is a component (rather than a
|
|
32562
|
-
// function that returns a string) is because it needs to use a
|
|
32563
|
-
// hook: `usePerseusI18n`.
|
|
32564
|
-
const i18n = usePerseusI18n();
|
|
32565
|
-
return describeSegmentGraph(state, i18n);
|
|
32566
|
-
}
|
|
32567
32771
|
|
|
32568
32772
|
// Exported for testing
|
|
32569
|
-
function
|
|
32773
|
+
function getSegmentGraphDescription(state, i18n) {
|
|
32570
32774
|
const {
|
|
32571
32775
|
strings,
|
|
32572
32776
|
locale
|
|
@@ -32583,13 +32787,13 @@ function describeSegmentGraph(state, i18n) {
|
|
|
32583
32787
|
});
|
|
32584
32788
|
}
|
|
32585
32789
|
|
|
32586
|
-
function renderSinusoidGraph(state, dispatch) {
|
|
32790
|
+
function renderSinusoidGraph(state, dispatch, i18n) {
|
|
32587
32791
|
return {
|
|
32588
32792
|
graph: /*#__PURE__*/React.createElement(SinusoidGraph, {
|
|
32589
32793
|
graphState: state,
|
|
32590
32794
|
dispatch: dispatch
|
|
32591
32795
|
}),
|
|
32592
|
-
interactiveElementsDescription:
|
|
32796
|
+
interactiveElementsDescription: getSinusoidDescription(state, i18n)
|
|
32593
32797
|
};
|
|
32594
32798
|
}
|
|
32595
32799
|
function SinusoidGraph(props) {
|
|
@@ -32597,6 +32801,9 @@ function SinusoidGraph(props) {
|
|
|
32597
32801
|
dispatch,
|
|
32598
32802
|
graphState
|
|
32599
32803
|
} = props;
|
|
32804
|
+
const i18n = usePerseusI18n();
|
|
32805
|
+
const id = React.useId();
|
|
32806
|
+
const descriptionId = id + "-description";
|
|
32600
32807
|
|
|
32601
32808
|
// Destructure the coordinates from the graph state
|
|
32602
32809
|
// Note: The order of the coordinates is important:
|
|
@@ -32621,15 +32828,30 @@ function SinusoidGraph(props) {
|
|
|
32621
32828
|
if (coeffs !== undefined) {
|
|
32622
32829
|
coeffRef.current = coeffs;
|
|
32623
32830
|
}
|
|
32624
|
-
|
|
32831
|
+
|
|
32832
|
+
// Aria strings
|
|
32833
|
+
const {
|
|
32834
|
+
srSinusoidGraph,
|
|
32835
|
+
srSinusoidDescription,
|
|
32836
|
+
srSinusoidRootPoint,
|
|
32837
|
+
srSinusoidPeakPoint
|
|
32838
|
+
} = describeSinusoidGraph(graphState, i18n);
|
|
32839
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
32840
|
+
// Outer graph minimal description
|
|
32841
|
+
"aria-label": srSinusoidGraph,
|
|
32842
|
+
"aria-describedby": descriptionId
|
|
32843
|
+
}, /*#__PURE__*/React.createElement(Plot$2.OfX, {
|
|
32625
32844
|
y: x => computeSine(x, coeffRef.current),
|
|
32626
32845
|
color: color.blue
|
|
32627
32846
|
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
32847
|
+
ariaLabel: i === 0 ? srSinusoidRootPoint : srSinusoidPeakPoint,
|
|
32628
32848
|
key: "point-" + i,
|
|
32629
32849
|
point: coord,
|
|
32630
32850
|
sequenceNumber: i + 1,
|
|
32631
32851
|
onMove: destination => dispatch(actions.sinusoid.movePoint(i, destination))
|
|
32632
|
-
}))
|
|
32852
|
+
})), /*#__PURE__*/React.createElement("g", {
|
|
32853
|
+
id: descriptionId
|
|
32854
|
+
}, srSinusoidDescription));
|
|
32633
32855
|
}
|
|
32634
32856
|
|
|
32635
32857
|
// Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
|
|
@@ -32667,6 +32889,52 @@ const getSinusoidCoefficients$1 = coords => {
|
|
|
32667
32889
|
verticalOffset
|
|
32668
32890
|
};
|
|
32669
32891
|
};
|
|
32892
|
+
function getSinusoidDescription(state, i18n) {
|
|
32893
|
+
const strings = describeSinusoidGraph(state, i18n);
|
|
32894
|
+
return strings.srSinusoidInteractiveElements;
|
|
32895
|
+
}
|
|
32896
|
+
function describeSinusoidGraph(state, i18n) {
|
|
32897
|
+
const {
|
|
32898
|
+
strings,
|
|
32899
|
+
locale
|
|
32900
|
+
} = i18n;
|
|
32901
|
+
const {
|
|
32902
|
+
coords
|
|
32903
|
+
} = state;
|
|
32904
|
+
const [root, peak] = coords;
|
|
32905
|
+
const diffX = Math.abs(peak[X] - root[X]);
|
|
32906
|
+
const diffY = Math.abs(peak[Y] - root[Y]);
|
|
32907
|
+
const srSinusoidGraph = strings.srSinusoidGraph;
|
|
32908
|
+
const srSinusoidDescription = strings.srSinusoidDescription({
|
|
32909
|
+
minValue: srFormatNumber(root[Y] - diffY, locale),
|
|
32910
|
+
maxValue: srFormatNumber(root[Y] + diffY, locale),
|
|
32911
|
+
cycleStart: srFormatNumber(root[X] - 2 * diffX, locale),
|
|
32912
|
+
cycleEnd: srFormatNumber(root[X] + 2 * diffX, locale)
|
|
32913
|
+
});
|
|
32914
|
+
const srSinusoidRootPoint = strings.srSinusoidRootPoint({
|
|
32915
|
+
x: srFormatNumber(root[X], locale),
|
|
32916
|
+
y: srFormatNumber(root[Y], locale)
|
|
32917
|
+
});
|
|
32918
|
+
const srSinusoidPeakPoint = strings.srSinusoidPeakPoint({
|
|
32919
|
+
x: srFormatNumber(peak[X], locale),
|
|
32920
|
+
y: srFormatNumber(peak[Y], locale)
|
|
32921
|
+
});
|
|
32922
|
+
const srSinusoidInteractiveElements = strings.srInteractiveElements({
|
|
32923
|
+
elements: strings.srSinusoidInteractiveElements({
|
|
32924
|
+
point1X: srFormatNumber(root[X], locale),
|
|
32925
|
+
point1Y: srFormatNumber(root[Y], locale),
|
|
32926
|
+
point2X: srFormatNumber(peak[X], locale),
|
|
32927
|
+
point2Y: srFormatNumber(peak[Y], locale)
|
|
32928
|
+
})
|
|
32929
|
+
});
|
|
32930
|
+
return {
|
|
32931
|
+
srSinusoidGraph,
|
|
32932
|
+
srSinusoidDescription,
|
|
32933
|
+
srSinusoidRootPoint,
|
|
32934
|
+
srSinusoidPeakPoint,
|
|
32935
|
+
srSinusoidInteractiveElements
|
|
32936
|
+
};
|
|
32937
|
+
}
|
|
32670
32938
|
|
|
32671
32939
|
const {
|
|
32672
32940
|
calculateAngleInDegrees,
|
|
@@ -32837,6 +33105,7 @@ const MafsGraph = props => {
|
|
|
32837
33105
|
const descriptionId = `interactive-graph-description-${uniqueId}`;
|
|
32838
33106
|
const interactiveElementsDescriptionId = `interactive-graph-interactive-elements-description-${uniqueId}`;
|
|
32839
33107
|
const unlimitedGraphKeyboardPromptId = `unlimited-graph-keyboard-prompt-${uniqueId}`;
|
|
33108
|
+
const instructionsId = `instructions-${uniqueId}`;
|
|
32840
33109
|
const graphRef = React.useRef(null);
|
|
32841
33110
|
const {
|
|
32842
33111
|
analytics
|
|
@@ -32864,6 +33133,7 @@ const MafsGraph = props => {
|
|
|
32864
33133
|
const interactionPrompt = isUnlimitedGraphState(state) && state.showKeyboardInteractionInvitation;
|
|
32865
33134
|
useOnMountEffect(() => {
|
|
32866
33135
|
analytics.onAnalyticsEvent({
|
|
33136
|
+
// TODO(LEMS-2827): Remove analytics event in LEMS-2827 in favor of ti below.
|
|
32867
33137
|
type: "perseus:interactive-graph-widget:rendered",
|
|
32868
33138
|
payload: {
|
|
32869
33139
|
type,
|
|
@@ -32871,6 +33141,14 @@ const MafsGraph = props => {
|
|
|
32871
33141
|
widgetId: "interactive-graph"
|
|
32872
33142
|
}
|
|
32873
33143
|
});
|
|
33144
|
+
analytics.onAnalyticsEvent({
|
|
33145
|
+
type: "perseus:widget:rendered:ti",
|
|
33146
|
+
payload: {
|
|
33147
|
+
widgetSubType: type,
|
|
33148
|
+
widgetType: "INTERACTIVE_GRAPH",
|
|
33149
|
+
widgetId: "interactive-graph"
|
|
33150
|
+
}
|
|
33151
|
+
});
|
|
32874
33152
|
});
|
|
32875
33153
|
const {
|
|
32876
33154
|
graph,
|
|
@@ -32878,7 +33156,8 @@ const MafsGraph = props => {
|
|
|
32878
33156
|
} = renderGraphElements({
|
|
32879
33157
|
state,
|
|
32880
33158
|
dispatch,
|
|
32881
|
-
i18n
|
|
33159
|
+
i18n,
|
|
33160
|
+
markings: props.markings
|
|
32882
33161
|
});
|
|
32883
33162
|
return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
|
|
32884
33163
|
value: {
|
|
@@ -32913,7 +33192,7 @@ const MafsGraph = props => {
|
|
|
32913
33192
|
handleKeyboardEvent(event, state, dispatch);
|
|
32914
33193
|
},
|
|
32915
33194
|
"aria-label": fullGraphAriaLabel,
|
|
32916
|
-
"aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId, isUnlimitedGraphState(state) && "
|
|
33195
|
+
"aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId, isUnlimitedGraphState(state) && unlimitedGraphKeyboardPromptId, state.type !== "none" && instructionsId),
|
|
32917
33196
|
ref: graphRef,
|
|
32918
33197
|
tabIndex: 0,
|
|
32919
33198
|
onFocus: event => {
|
|
@@ -32930,7 +33209,11 @@ const MafsGraph = props => {
|
|
|
32930
33209
|
id: interactiveElementsDescriptionId,
|
|
32931
33210
|
tabIndex: -1,
|
|
32932
33211
|
className: "mafs-sr-only"
|
|
32933
|
-
}, interactiveElementsDescription), /*#__PURE__*/React.createElement(
|
|
33212
|
+
}, interactiveElementsDescription), state.type !== "none" && /*#__PURE__*/React.createElement(View, {
|
|
33213
|
+
id: instructionsId,
|
|
33214
|
+
tabIndex: -1,
|
|
33215
|
+
className: "mafs-sr-only"
|
|
33216
|
+
}, isUnlimitedGraphState(state) ? strings.srUnlimitedGraphInstructions : strings.srGraphInstructions), /*#__PURE__*/React.createElement(LegacyGrid, {
|
|
32934
33217
|
box: props.box,
|
|
32935
33218
|
backgroundImage: props.backgroundImage
|
|
32936
33219
|
}), /*#__PURE__*/React.createElement(View, {
|
|
@@ -32939,7 +33222,21 @@ const MafsGraph = props => {
|
|
|
32939
33222
|
bottom: 0,
|
|
32940
33223
|
left: 0
|
|
32941
33224
|
}
|
|
32942
|
-
}, (props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels,
|
|
33225
|
+
}, (props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, {
|
|
33226
|
+
i18n: i18n
|
|
33227
|
+
})), /*#__PURE__*/React.createElement(View
|
|
33228
|
+
// If we have locked figures, we set aria-hidden
|
|
33229
|
+
// to false so that screen readers can read the
|
|
33230
|
+
// locked figures. If we don't have any locked
|
|
33231
|
+
// figures, we need to set aria-hidden to true so
|
|
33232
|
+
// that screen readers don't read this Mafs element
|
|
33233
|
+
// as an empty image.
|
|
33234
|
+
// Note: Adding this in a wrapping View element
|
|
33235
|
+
// so that `aria-hidden` is a valid property.
|
|
33236
|
+
// It does not work on `Mafs` or svg elements.
|
|
33237
|
+
, {
|
|
33238
|
+
"aria-hidden": props.lockedFigures && props.lockedFigures.length > 0 ? false : true
|
|
33239
|
+
}, /*#__PURE__*/React.createElement(Mafs, {
|
|
32943
33240
|
preserveAspectRatio: false,
|
|
32944
33241
|
viewBox: {
|
|
32945
33242
|
x: state.range[X],
|
|
@@ -32959,10 +33256,10 @@ const MafsGraph = props => {
|
|
|
32959
33256
|
height: height
|
|
32960
33257
|
})),
|
|
32961
33258
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
32962
|
-
(props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes,
|
|
33259
|
+
(props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), props.lockedFigures && props.lockedFigures.length > 0 && /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, /*#__PURE__*/React.createElement(GraphLockedLayer, {
|
|
32963
33260
|
lockedFigures: props.lockedFigures,
|
|
32964
33261
|
range: state.range
|
|
32965
|
-
}))), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
|
|
33262
|
+
})))), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
|
|
32966
33263
|
lockedFigures: props.lockedFigures
|
|
32967
33264
|
}), /*#__PURE__*/React.createElement(View, {
|
|
32968
33265
|
style: {
|
|
@@ -33257,32 +33554,33 @@ const renderGraphElements = props => {
|
|
|
33257
33554
|
const {
|
|
33258
33555
|
state,
|
|
33259
33556
|
dispatch,
|
|
33260
|
-
i18n
|
|
33557
|
+
i18n,
|
|
33558
|
+
markings
|
|
33261
33559
|
} = props;
|
|
33262
33560
|
const {
|
|
33263
33561
|
type
|
|
33264
33562
|
} = state;
|
|
33265
33563
|
switch (type) {
|
|
33266
33564
|
case "angle":
|
|
33267
|
-
return renderAngleGraph(state, dispatch);
|
|
33565
|
+
return renderAngleGraph(state, dispatch, i18n);
|
|
33268
33566
|
case "segment":
|
|
33269
|
-
return renderSegmentGraph(state, dispatch);
|
|
33567
|
+
return renderSegmentGraph(state, dispatch, i18n);
|
|
33270
33568
|
case "linear-system":
|
|
33271
|
-
return renderLinearSystemGraph(state, dispatch);
|
|
33569
|
+
return renderLinearSystemGraph(state, dispatch, i18n);
|
|
33272
33570
|
case "linear":
|
|
33273
|
-
return renderLinearGraph(state, dispatch);
|
|
33571
|
+
return renderLinearGraph(state, dispatch, i18n);
|
|
33274
33572
|
case "ray":
|
|
33275
|
-
return renderRayGraph(state, dispatch);
|
|
33573
|
+
return renderRayGraph(state, dispatch, i18n);
|
|
33276
33574
|
case "polygon":
|
|
33277
|
-
return renderPolygonGraph(state, dispatch);
|
|
33575
|
+
return renderPolygonGraph(state, dispatch, i18n, markings);
|
|
33278
33576
|
case "point":
|
|
33279
|
-
return renderPointGraph(state, dispatch);
|
|
33577
|
+
return renderPointGraph(state, dispatch, i18n);
|
|
33280
33578
|
case "circle":
|
|
33281
|
-
return renderCircleGraph(state, dispatch);
|
|
33579
|
+
return renderCircleGraph(state, dispatch, i18n);
|
|
33282
33580
|
case "quadratic":
|
|
33283
33581
|
return renderQuadraticGraph(state, dispatch, i18n);
|
|
33284
33582
|
case "sinusoid":
|
|
33285
|
-
return renderSinusoidGraph(state, dispatch);
|
|
33583
|
+
return renderSinusoidGraph(state, dispatch, i18n);
|
|
33286
33584
|
case "none":
|
|
33287
33585
|
return {
|
|
33288
33586
|
graph: null,
|
|
@@ -33679,13 +33977,13 @@ const {
|
|
|
33679
33977
|
polar
|
|
33680
33978
|
} = angles;
|
|
33681
33979
|
const {
|
|
33682
|
-
angleMeasures
|
|
33683
|
-
ccw
|
|
33684
|
-
lawOfCosines
|
|
33980
|
+
angleMeasures,
|
|
33981
|
+
ccw,
|
|
33982
|
+
lawOfCosines,
|
|
33685
33983
|
magnitude: magnitude$1,
|
|
33686
33984
|
polygonSidesIntersect,
|
|
33687
33985
|
reverseVector,
|
|
33688
|
-
sign
|
|
33986
|
+
sign,
|
|
33689
33987
|
vector: vector$1
|
|
33690
33988
|
} = geometry;
|
|
33691
33989
|
const {
|
|
@@ -34169,7 +34467,7 @@ function doAddPoint(state, action) {
|
|
|
34169
34467
|
return _extends({}, state, {
|
|
34170
34468
|
hasBeenInteractedWith: true,
|
|
34171
34469
|
coords: newCoords,
|
|
34172
|
-
showRemovePointButton:
|
|
34470
|
+
showRemovePointButton: true,
|
|
34173
34471
|
focusedPointIndex: newCoords.length - 1
|
|
34174
34472
|
});
|
|
34175
34473
|
}
|
|
@@ -34177,16 +34475,11 @@ function doRemovePoint(state, action) {
|
|
|
34177
34475
|
if (!isUnlimitedGraphState(state)) {
|
|
34178
34476
|
return state;
|
|
34179
34477
|
}
|
|
34180
|
-
|
|
34181
|
-
if (state.interactionMode === "mouse") {
|
|
34182
|
-
nextFocusedPointIndex = null;
|
|
34183
|
-
} else {
|
|
34184
|
-
nextFocusedPointIndex = state.coords.length > 1 ? state.coords.length - 2 : null;
|
|
34185
|
-
}
|
|
34478
|
+
const nextFocusedPointIndex = state.coords.length > 1 ? state.coords.length - 2 : null;
|
|
34186
34479
|
return _extends({}, state, {
|
|
34187
34480
|
coords: state.coords.filter((_, i) => i !== action.index),
|
|
34188
34481
|
focusedPointIndex: nextFocusedPointIndex,
|
|
34189
|
-
showRemovePointButton: false
|
|
34482
|
+
showRemovePointButton: nextFocusedPointIndex !== null ? true : false
|
|
34190
34483
|
});
|
|
34191
34484
|
}
|
|
34192
34485
|
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
@@ -34210,7 +34503,7 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
34210
34503
|
return [dx, dy];
|
|
34211
34504
|
};
|
|
34212
34505
|
// Less than or approximately equal
|
|
34213
|
-
function leq
|
|
34506
|
+
function leq(a, b) {
|
|
34214
34507
|
return a < b || approximateEqual(a, b);
|
|
34215
34508
|
}
|
|
34216
34509
|
function boundAndSnapToGrid(point, {
|
|
@@ -34355,7 +34648,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, {
|
|
|
34355
34648
|
});
|
|
34356
34649
|
|
|
34357
34650
|
// Gets the radian angles between the coords and maps them to degrees
|
|
34358
|
-
const angles = angleMeasures
|
|
34651
|
+
const angles = angleMeasures(coordsCopy).map(angle => angle * 180 / Math.PI);
|
|
34359
34652
|
|
|
34360
34653
|
// Gets the relative index of a point
|
|
34361
34654
|
const rel = j => {
|
|
@@ -34375,7 +34668,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, {
|
|
|
34375
34668
|
|
|
34376
34669
|
// Avoid degenerate triangles
|
|
34377
34670
|
if (innerAngles.some(function (angle) {
|
|
34378
|
-
return leq
|
|
34671
|
+
return leq(angle, 1);
|
|
34379
34672
|
})) {
|
|
34380
34673
|
return startingPoint;
|
|
34381
34674
|
}
|
|
@@ -34389,7 +34682,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, {
|
|
|
34389
34682
|
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
34390
34683
|
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
34391
34684
|
// for is then used in the polar function to determine the new point.
|
|
34392
|
-
const onLeft = sign
|
|
34685
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
34393
34686
|
|
|
34394
34687
|
// Solve for side by using the law of sines
|
|
34395
34688
|
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
@@ -34436,13 +34729,13 @@ function boundAndSnapToSides(destinationPoint, {
|
|
|
34436
34729
|
});
|
|
34437
34730
|
|
|
34438
34731
|
// Avoid degenerate triangles
|
|
34439
|
-
if (leq
|
|
34732
|
+
if (leq(sides[1] + sides[2], sides[0]) || leq(sides[0] + sides[2], sides[1]) || leq(sides[0] + sides[1], sides[2])) {
|
|
34440
34733
|
return startingPoint;
|
|
34441
34734
|
}
|
|
34442
34735
|
|
|
34443
34736
|
// Solve for angle by using the law of cosines
|
|
34444
34737
|
// Angle at the first vertex of the polygon
|
|
34445
|
-
const innerAngle = lawOfCosines
|
|
34738
|
+
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
34446
34739
|
|
|
34447
34740
|
// Angle at the second vertex of the polygon
|
|
34448
34741
|
const outerAngle = getAngleFromVertex(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
@@ -34453,7 +34746,7 @@ function boundAndSnapToSides(destinationPoint, {
|
|
|
34453
34746
|
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
34454
34747
|
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
34455
34748
|
// for is then used in the polar function to determine the new point.
|
|
34456
|
-
const onLeft = sign
|
|
34749
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
34457
34750
|
|
|
34458
34751
|
// Uses the length of the first side of the polygon (radial coordinate)
|
|
34459
34752
|
// and the angle between the first and second sides of the
|
|
@@ -34588,19 +34881,11 @@ const {
|
|
|
34588
34881
|
getClockwiseAngle
|
|
34589
34882
|
} = angles;
|
|
34590
34883
|
const {
|
|
34591
|
-
angleMeasures,
|
|
34592
|
-
ccw,
|
|
34593
|
-
collinear,
|
|
34594
34884
|
getLineEquation,
|
|
34595
34885
|
getLineIntersection,
|
|
34596
|
-
intersects,
|
|
34597
|
-
lawOfCosines,
|
|
34598
34886
|
magnitude,
|
|
34599
|
-
rotate,
|
|
34600
|
-
sign,
|
|
34601
34887
|
vector
|
|
34602
34888
|
} = geometry;
|
|
34603
|
-
const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
|
|
34604
34889
|
const defaultBackgroundImage = {
|
|
34605
34890
|
url: null
|
|
34606
34891
|
};
|
|
@@ -34609,31 +34894,9 @@ const UNLIMITED = "unlimited";
|
|
|
34609
34894
|
// Sample background image:
|
|
34610
34895
|
// https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
|
|
34611
34896
|
|
|
34612
|
-
// default to defaultValue if actual is null or undefined
|
|
34613
|
-
function defaultVal(actual, defaultValue) {
|
|
34614
|
-
return actual == null ? defaultValue : actual;
|
|
34615
|
-
}
|
|
34616
|
-
|
|
34617
|
-
// Less than or approximately equal
|
|
34618
|
-
function leq(a, b) {
|
|
34619
|
-
return a < b || approximateEqual(a, b);
|
|
34620
|
-
}
|
|
34621
|
-
function capitalize(str) {
|
|
34622
|
-
return str.replace(/(?:^|-)(.)/g, function (match, letter) {
|
|
34623
|
-
return letter.toUpperCase();
|
|
34624
|
-
});
|
|
34625
|
-
}
|
|
34626
34897
|
function numSteps(range, step) {
|
|
34627
34898
|
return Math.floor((range[1] - range[0]) / step);
|
|
34628
34899
|
}
|
|
34629
|
-
const _getShouldShowInstructions = props => {
|
|
34630
|
-
return _isClickToAddPoints(props) && (
|
|
34631
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
34632
|
-
props.graph.coords == null || props.graph.coords.length === 0);
|
|
34633
|
-
};
|
|
34634
|
-
const _isClickToAddPoints = props => {
|
|
34635
|
-
return props.graph.type === "point" && props.graph.numPoints === UNLIMITED || props.graph.type === "polygon" && props.graph.numSides === UNLIMITED;
|
|
34636
|
-
};
|
|
34637
34900
|
const makeInvalidTypeError = (functionName, graphType) => {
|
|
34638
34901
|
return new PerseusError(`${functionName} called but current graph type is not a '${graphType}'`, Errors.NotAllowed, {
|
|
34639
34902
|
metadata: {
|
|
@@ -34674,1236 +34937,16 @@ function getQuadraticCoefficients(coords) {
|
|
|
34674
34937
|
const c = (p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] + p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] + p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) / denom;
|
|
34675
34938
|
return [a, b, c];
|
|
34676
34939
|
}
|
|
34677
|
-
|
|
34678
|
-
// (LEMS-2190): Move the Mafs Angle Graph coordinate reversal logic in interactive-graph-state.ts
|
|
34679
|
-
// to this file when we remove the legacy graph. This logic allows us to support bi-directional angles
|
|
34680
|
-
// for the new (non-reflexive) Mafs graphs, while maintaining the same scoring behaviour as the legacy graph.
|
|
34681
|
-
// Once the legacy graph is removed, we should move this logic directly into the validate function below.
|
|
34682
|
-
class LegacyInteractiveGraph extends React.Component {
|
|
34683
|
-
constructor(...args) {
|
|
34684
|
-
super(...args);
|
|
34685
|
-
this.angle = void 0;
|
|
34686
|
-
this.circle = void 0;
|
|
34687
|
-
this.graphie = void 0;
|
|
34688
|
-
this.horizHairline = void 0;
|
|
34689
|
-
this.line = void 0;
|
|
34690
|
-
this.lines = void 0;
|
|
34691
|
-
this.parabola = void 0;
|
|
34692
|
-
this.pointA = void 0;
|
|
34693
|
-
this.pointB = void 0;
|
|
34694
|
-
this.pointC = void 0;
|
|
34695
|
-
// @ts-expect-error - TS2564 - Property 'points' has no initializer and is not definitely assigned in the constructor.
|
|
34696
|
-
this.points = void 0;
|
|
34697
|
-
this.polygon = void 0;
|
|
34698
|
-
// @ts-expect-error - TS2564 - Property 'shouldResetGraphie' has no initializer and is not definitely assigned in the constructor.
|
|
34699
|
-
this.shouldResetGraphie = void 0;
|
|
34700
|
-
this.sinusoid = void 0;
|
|
34701
|
-
this.trashCan = void 0;
|
|
34702
|
-
this.vertHairline = void 0;
|
|
34703
|
-
this.state = {
|
|
34704
|
-
shouldShowInstructions: _getShouldShowInstructions(this.props)
|
|
34705
|
-
};
|
|
34706
|
-
this._getShouldShowInstructions = props => {
|
|
34707
|
-
props = props || this.props;
|
|
34708
|
-
return this.isClickToAddPoints(props) && (
|
|
34709
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
34710
|
-
props.graph.coords == null || props.graph.coords.length === 0);
|
|
34711
|
-
};
|
|
34712
|
-
this.setGraphie = newGraphie => {
|
|
34713
|
-
this.graphie = newGraphie;
|
|
34714
|
-
this.setupGraphie();
|
|
34715
|
-
};
|
|
34716
|
-
this.handleAddPointsMouseDown = coord => {
|
|
34717
|
-
// This function should only be called when this.isClickToAddPoints()
|
|
34718
|
-
// is true
|
|
34719
|
-
if (!this.isClickToAddPoints()) {
|
|
34720
|
-
throw new PerseusError("handleAddPointsClick should not be registered" + "when isClickToAddPoints() is false", Errors.NotAllowed);
|
|
34721
|
-
}
|
|
34722
|
-
if (!this.isCoordInTrash(coord)) {
|
|
34723
|
-
let point;
|
|
34724
|
-
if (this.props.graph.type === "point") {
|
|
34725
|
-
point = this.createPointForPointsType(coord, this.points.length);
|
|
34726
|
-
if (!point.constrain()) {
|
|
34727
|
-
point.remove();
|
|
34728
|
-
return;
|
|
34729
|
-
}
|
|
34730
|
-
this.points.push(point);
|
|
34731
|
-
|
|
34732
|
-
// interactive2 allows us to grab the point
|
|
34733
|
-
const idx = this.points.length - 1;
|
|
34734
|
-
this.points[idx].grab(coord);
|
|
34735
|
-
this.updateCoordsFromPoints();
|
|
34736
|
-
} else if (this.props.graph.type === "polygon") {
|
|
34737
|
-
var _this$polygon;
|
|
34738
|
-
if ((_this$polygon = this.polygon) != null && _this$polygon.closed()) {
|
|
34739
|
-
return;
|
|
34740
|
-
}
|
|
34741
|
-
point = this.createPointForPolygonType(coord, this.points.length);
|
|
34742
|
-
this.points.push(point);
|
|
34743
|
-
const idx = this.points.length - 1;
|
|
34744
|
-
this.points[idx].grab(coord);
|
|
34745
|
-
|
|
34746
|
-
// We don't call updateCoordsFromPoints for
|
|
34747
|
-
// polygons, since the polygon won't be
|
|
34748
|
-
// closed yet.
|
|
34749
|
-
this.updatePolygon();
|
|
34750
|
-
}
|
|
34751
|
-
this.setState({
|
|
34752
|
-
shouldShowInstructions: false
|
|
34753
|
-
});
|
|
34754
|
-
}
|
|
34755
|
-
};
|
|
34756
|
-
this.resetGraphie = () => {
|
|
34757
|
-
var _this$refs$graph;
|
|
34758
|
-
this.shouldResetGraphie = false;
|
|
34759
|
-
this.parabola = null;
|
|
34760
|
-
this.sinusoid = null;
|
|
34761
|
-
// eslint-disable-next-line react/no-string-refs
|
|
34762
|
-
// @ts-expect-error - TS2339 - Property 'reset' does not exist on type 'ReactInstance'.
|
|
34763
|
-
(_this$refs$graph = this.refs.graph) == null || _this$refs$graph.reset();
|
|
34764
|
-
};
|
|
34765
|
-
this.setupGraphie = () => {
|
|
34766
|
-
this.setTrashCanVisibility(0);
|
|
34767
|
-
if (this.isClickToAddPoints()) {
|
|
34768
|
-
this.setTrashCanVisibility(0.5);
|
|
34769
|
-
}
|
|
34770
|
-
if (this.props.apiOptions.isMobile) {
|
|
34771
|
-
var _this$horizHairline, _this$vertHairline;
|
|
34772
|
-
this.horizHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
|
|
34773
|
-
normalStyle: {
|
|
34774
|
-
strokeWidth: 1
|
|
34775
|
-
}
|
|
34776
|
-
});
|
|
34777
|
-
this.horizHairline.attr({
|
|
34778
|
-
stroke: KhanColors.INTERACTIVE
|
|
34779
|
-
});
|
|
34780
|
-
(_this$horizHairline = this.horizHairline) == null || _this$horizHairline.hide();
|
|
34781
|
-
this.vertHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
|
|
34782
|
-
normalStyle: {
|
|
34783
|
-
strokeWidth: 1
|
|
34784
|
-
}
|
|
34785
|
-
});
|
|
34786
|
-
this.vertHairline.attr({
|
|
34787
|
-
stroke: KhanColors.INTERACTIVE
|
|
34788
|
-
});
|
|
34789
|
-
(_this$vertHairline = this.vertHairline) == null || _this$vertHairline.hide();
|
|
34790
|
-
}
|
|
34791
|
-
const type = this.props.graph.type;
|
|
34792
|
-
this["add" + capitalize(type) + "Controls"]();
|
|
34793
|
-
};
|
|
34794
|
-
this.showHairlines = point => {
|
|
34795
|
-
if (this.props.apiOptions.isMobile && this.props.markings !== "none") {
|
|
34796
|
-
var _this$horizHairline2, _this$horizHairline3, _this$vertHairline2, _this$vertHairline3;
|
|
34797
|
-
// Hairlines are already initialized when the graph is loaded, so
|
|
34798
|
-
// here we just move them to the updated location and make them
|
|
34799
|
-
// visible.
|
|
34800
|
-
(_this$horizHairline2 = this.horizHairline) == null || _this$horizHairline2.moveTo([this.props.range[0][0], point[1]], [this.props.range[0][1], point[1]]);
|
|
34801
|
-
(_this$horizHairline3 = this.horizHairline) == null || _this$horizHairline3.show();
|
|
34802
|
-
(_this$vertHairline2 = this.vertHairline) == null || _this$vertHairline2.moveTo([point[0], this.props.range[1][0]], [point[0], this.props.range[1][1]]);
|
|
34803
|
-
(_this$vertHairline3 = this.vertHairline) == null || _this$vertHairline3.show();
|
|
34804
|
-
}
|
|
34805
|
-
};
|
|
34806
|
-
this.hideHairlines = () => {
|
|
34807
|
-
if (this.props.apiOptions.isMobile) {
|
|
34808
|
-
var _this$horizHairline4, _this$vertHairline4;
|
|
34809
|
-
(_this$horizHairline4 = this.horizHairline) == null || _this$horizHairline4.hide();
|
|
34810
|
-
(_this$vertHairline4 = this.vertHairline) == null || _this$vertHairline4.hide();
|
|
34811
|
-
}
|
|
34812
|
-
};
|
|
34813
|
-
this.setTrashCanVisibility = opacity => {
|
|
34814
|
-
const graphie = this.graphie;
|
|
34815
|
-
if (number.equal(opacity, 0)) {
|
|
34816
|
-
if (this.trashCan) {
|
|
34817
|
-
this.trashCan.remove();
|
|
34818
|
-
this.trashCan = null;
|
|
34819
|
-
}
|
|
34820
|
-
} else if (!this.props.apiOptions.isMobile) {
|
|
34821
|
-
var _this$trashCan;
|
|
34822
|
-
// Only if trash tooltips are not being used, we initialize the old
|
|
34823
|
-
// trash can area.
|
|
34824
|
-
if (!this.trashCan) {
|
|
34825
|
-
var _graphie$raphael;
|
|
34826
|
-
this.trashCan = graphie == null || (_graphie$raphael = graphie.raphael) == null ? void 0 : _graphie$raphael.image(TRASH_ICON_URI, graphie.xpixels - 40, graphie.ypixels - 40, 40, 40);
|
|
34827
|
-
}
|
|
34828
|
-
(_this$trashCan = this.trashCan) == null || _this$trashCan.attr({
|
|
34829
|
-
opacity: opacity
|
|
34830
|
-
});
|
|
34831
|
-
}
|
|
34832
|
-
};
|
|
34833
|
-
this.isClickToAddPoints = props => {
|
|
34834
|
-
props = props || this.props;
|
|
34835
|
-
return _isClickToAddPoints(props);
|
|
34836
|
-
};
|
|
34837
|
-
this._lineStroke = () => {
|
|
34838
|
-
// This should probably use: this.props.apiOptions.isMobile
|
|
34839
|
-
// @ts-expect-error - TS2339 - Property 'isMobile' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
|
|
34840
|
-
return this.props.isMobile ? {
|
|
34841
|
-
"stroke-width": 3
|
|
34842
|
-
} : {};
|
|
34843
|
-
};
|
|
34844
|
-
this.addLine = type => {
|
|
34845
|
-
const self = this;
|
|
34846
|
-
const graphie = self.graphie;
|
|
34847
|
-
const coords = InteractiveGraph.getLineCoords(self.props.graph, self.props);
|
|
34848
|
-
const points = self.points = _.map(coords, coord => {
|
|
34849
|
-
return Interactive2.addMaybeMobileMovablePoint(this, {
|
|
34850
|
-
coord: coord,
|
|
34851
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap()],
|
|
34852
|
-
onMove: () => {
|
|
34853
|
-
const graph = _.extend({}, self.props.graph, {
|
|
34854
|
-
coords: _.invoke(points, "coord")
|
|
34855
|
-
});
|
|
34856
|
-
self.onChange({
|
|
34857
|
-
graph: graph
|
|
34858
|
-
});
|
|
34859
|
-
}
|
|
34860
|
-
});
|
|
34861
|
-
});
|
|
34862
|
-
const lineConfig = {
|
|
34863
|
-
points: points,
|
|
34864
|
-
static: true,
|
|
34865
|
-
normalStyle: _extends({
|
|
34866
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE
|
|
34867
|
-
}, this._lineStroke())
|
|
34868
|
-
};
|
|
34869
|
-
if (type === "line") {
|
|
34870
|
-
// @ts-expect-error - TS2339 - Property 'extendLine' does not exist on type '{ readonly points: any[]; readonly static: true; readonly normalStyle: { readonly "stroke-width"?: number | undefined; readonly stroke: "#71B307" | "#63D9EA"; }; }'.
|
|
34871
|
-
lineConfig.extendLine = true;
|
|
34872
|
-
} else if (type === "ray") {
|
|
34873
|
-
// @ts-expect-error - TS2339 - Property 'extendRay' does not exist on type '{ readonly points: any[]; readonly static: true; readonly normalStyle: { readonly "stroke-width"?: number | undefined; readonly stroke: "#71B307" | "#63D9EA"; }; }'.
|
|
34874
|
-
lineConfig.extendRay = true;
|
|
34875
|
-
}
|
|
34876
|
-
self.line = Interactive2.addMovableLine(graphie, lineConfig);
|
|
34877
|
-
|
|
34878
|
-
// A and B can't be in the same place
|
|
34879
|
-
points[0].listen("constraints", "isLine", coord => {
|
|
34880
|
-
return !point.equal(coord, points[1].coord());
|
|
34881
|
-
});
|
|
34882
|
-
points[1].listen("constraints", "isLine", coord => {
|
|
34883
|
-
return !point.equal(coord, points[0].coord());
|
|
34884
|
-
});
|
|
34885
|
-
};
|
|
34886
|
-
this.removeLine = () => {
|
|
34887
|
-
var _this$line;
|
|
34888
|
-
_.invoke(this.points, "remove");
|
|
34889
|
-
(_this$line = this.line) == null || _this$line.remove();
|
|
34890
|
-
};
|
|
34891
|
-
this.addNoneControls = () => {};
|
|
34892
|
-
this.removeNoneControls = () => {};
|
|
34893
|
-
this.addLinearControls = () => {
|
|
34894
|
-
this.addLine("line");
|
|
34895
|
-
};
|
|
34896
|
-
this.removeLinearControls = () => {
|
|
34897
|
-
this.removeLine();
|
|
34898
|
-
};
|
|
34899
|
-
this.addQuadraticControls = () => {
|
|
34900
|
-
if (this.props.graph.type !== "quadratic") {
|
|
34901
|
-
throw makeInvalidTypeError("addQuadraticControls", "quadratic");
|
|
34902
|
-
}
|
|
34903
|
-
let coords = this.props.graph.coords;
|
|
34904
|
-
if (!coords) {
|
|
34905
|
-
coords = InteractiveGraph.defaultQuadraticCoords(this.props);
|
|
34906
|
-
}
|
|
34907
|
-
const onMoveHandler = () => {
|
|
34908
|
-
const graph = _.extend({}, this.props.graph, {
|
|
34909
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'.
|
|
34910
|
-
coords: [pointA.coord(), pointB.coord(), pointC.coord()]
|
|
34911
|
-
});
|
|
34912
|
-
this.onChange({
|
|
34913
|
-
graph
|
|
34914
|
-
});
|
|
34915
|
-
this.updateQuadratic();
|
|
34916
|
-
};
|
|
34917
|
-
|
|
34918
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
34919
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
34920
|
-
// to be executed synchronously, which causes this issue.
|
|
34921
|
-
let pointA = null;
|
|
34922
|
-
pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
34923
|
-
coord: coords[0],
|
|
34924
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
34925
|
-
return !pointA ||
|
|
34926
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
34927
|
-
coord[0] !== pointB.coord()[0] &&
|
|
34928
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
34929
|
-
coord[0] !== pointC.coord()[0];
|
|
34930
|
-
}],
|
|
34931
|
-
onMove: onMoveHandler
|
|
34932
|
-
});
|
|
34933
|
-
|
|
34934
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
34935
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
34936
|
-
// to be executed synchronously, which causes this issue.
|
|
34937
|
-
let pointB = null;
|
|
34938
|
-
pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
34939
|
-
coord: coords[1],
|
|
34940
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
34941
|
-
return !pointB ||
|
|
34942
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
34943
|
-
coord[0] !== pointA.coord()[0] &&
|
|
34944
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
34945
|
-
coord[0] !== pointC.coord()[0];
|
|
34946
|
-
}],
|
|
34947
|
-
onMove: onMoveHandler
|
|
34948
|
-
});
|
|
34949
|
-
|
|
34950
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
34951
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
34952
|
-
// to be executed synchronously, which causes this issue.
|
|
34953
|
-
let pointC = null;
|
|
34954
|
-
pointC = this.pointC = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
34955
|
-
coord: coords[2],
|
|
34956
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
34957
|
-
return !pointC ||
|
|
34958
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
34959
|
-
coord[0] !== pointA.coord()[0] &&
|
|
34960
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
34961
|
-
coord[0] !== pointB.coord()[0];
|
|
34962
|
-
}],
|
|
34963
|
-
onMove: onMoveHandler
|
|
34964
|
-
});
|
|
34965
|
-
this.updateQuadratic();
|
|
34966
|
-
};
|
|
34967
|
-
this.updateQuadratic = () => {
|
|
34968
|
-
const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(this.props);
|
|
34969
|
-
if (!coeffs) {
|
|
34970
|
-
return;
|
|
34971
|
-
}
|
|
34972
|
-
|
|
34973
|
-
// Extract coefficients the parabola
|
|
34974
|
-
const a = coeffs[0];
|
|
34975
|
-
const b = coeffs[1];
|
|
34976
|
-
const c = coeffs[2];
|
|
34977
|
-
|
|
34978
|
-
// Plot and style
|
|
34979
|
-
if (this.parabola) {
|
|
34980
|
-
var _this$graphie, _this$parabola;
|
|
34981
|
-
const path = (_this$graphie = this.graphie) == null ? void 0 : _this$graphie.svgParabolaPath(a, b, c);
|
|
34982
|
-
(_this$parabola = this.parabola) == null || _this$parabola.attr({
|
|
34983
|
-
path: path
|
|
34984
|
-
});
|
|
34985
|
-
} else {
|
|
34986
|
-
var _this$graphie2, _this$parabola2, _this$parabola3;
|
|
34987
|
-
this.parabola = (_this$graphie2 = this.graphie) == null ? void 0 : _this$graphie2.parabola(a, b, c);
|
|
34988
|
-
(_this$parabola2 = this.parabola) == null || _this$parabola2.attr(_extends({
|
|
34989
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.DYNAMIC
|
|
34990
|
-
}, this._lineStroke()));
|
|
34991
|
-
(_this$parabola3 = this.parabola) == null || _this$parabola3.toBack();
|
|
34992
|
-
}
|
|
34993
|
-
};
|
|
34994
|
-
this.removeQuadraticControls = () => {
|
|
34995
|
-
var _this$pointA, _this$pointB, _this$pointC;
|
|
34996
|
-
(_this$pointA = this.pointA) == null || _this$pointA.remove();
|
|
34997
|
-
(_this$pointB = this.pointB) == null || _this$pointB.remove();
|
|
34998
|
-
(_this$pointC = this.pointC) == null || _this$pointC.remove();
|
|
34999
|
-
if (this.parabola) {
|
|
35000
|
-
this.parabola.remove();
|
|
35001
|
-
this.parabola = null;
|
|
35002
|
-
}
|
|
35003
|
-
};
|
|
35004
|
-
this.addSinusoidControls = () => {
|
|
35005
|
-
if (this.props.graph.type !== "sinusoid") {
|
|
35006
|
-
throw makeInvalidTypeError("addSinusoidControls", "sinusoid");
|
|
35007
|
-
}
|
|
35008
|
-
let coords = this.props.graph.coords;
|
|
35009
|
-
if (!coords) {
|
|
35010
|
-
coords = InteractiveGraph.defaultSinusoidCoords(this.props);
|
|
35011
|
-
}
|
|
35012
|
-
const onMoveHandler = () => {
|
|
35013
|
-
const graph = _.extend({}, this.props.graph, {
|
|
35014
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35015
|
-
coords: [pointA.coord(), pointB.coord()]
|
|
35016
|
-
});
|
|
35017
|
-
this.onChange({
|
|
35018
|
-
graph: graph
|
|
35019
|
-
});
|
|
35020
|
-
this.updateSinusoid();
|
|
35021
|
-
};
|
|
35022
|
-
|
|
35023
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35024
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35025
|
-
// to be executed synchronously, which causes this issue.
|
|
35026
|
-
let pointA = null;
|
|
35027
|
-
pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35028
|
-
coord: coords[0],
|
|
35029
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35030
|
-
return !pointA || coord[0] !== pointB.coord()[0];
|
|
35031
|
-
}],
|
|
35032
|
-
onMove: onMoveHandler
|
|
35033
|
-
});
|
|
35034
|
-
const pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35035
|
-
coord: coords[1],
|
|
35036
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35037
|
-
// @ts-expect-error - TS2339 - Property 'coord' does not exist on type 'never'.
|
|
35038
|
-
return !pointA || coord[0] !== pointA.coord()[0];
|
|
35039
|
-
}],
|
|
35040
|
-
onMove: onMoveHandler
|
|
35041
|
-
});
|
|
35042
|
-
this.updateSinusoid();
|
|
35043
|
-
};
|
|
35044
|
-
this.updateSinusoid = () => {
|
|
35045
|
-
const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(this.props);
|
|
35046
|
-
if (!coeffs) {
|
|
35047
|
-
return;
|
|
35048
|
-
}
|
|
35049
|
-
const a = coeffs[0];
|
|
35050
|
-
const b = coeffs[1];
|
|
35051
|
-
const c = coeffs[2];
|
|
35052
|
-
const d = coeffs[3];
|
|
35053
|
-
|
|
35054
|
-
// Plot and style
|
|
35055
|
-
if (this.sinusoid) {
|
|
35056
|
-
var _this$graphie3, _this$sinusoid;
|
|
35057
|
-
const path = (_this$graphie3 = this.graphie) == null ? void 0 : _this$graphie3.svgSinusoidPath(a, b, c, d);
|
|
35058
|
-
(_this$sinusoid = this.sinusoid) == null || _this$sinusoid.attr({
|
|
35059
|
-
path: path
|
|
35060
|
-
});
|
|
35061
|
-
} else {
|
|
35062
|
-
var _this$graphie4, _this$sinusoid2, _this$sinusoid3;
|
|
35063
|
-
this.sinusoid = (_this$graphie4 = this.graphie) == null ? void 0 : _this$graphie4.sinusoid(a, b, c, d);
|
|
35064
|
-
(_this$sinusoid2 = this.sinusoid) == null || _this$sinusoid2.attr(_extends({
|
|
35065
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.DYNAMIC
|
|
35066
|
-
}, this._lineStroke()));
|
|
35067
|
-
(_this$sinusoid3 = this.sinusoid) == null || _this$sinusoid3.toBack();
|
|
35068
|
-
}
|
|
35069
|
-
};
|
|
35070
|
-
this.removeSinusoidControls = () => {
|
|
35071
|
-
var _this$pointA2, _this$pointB2;
|
|
35072
|
-
(_this$pointA2 = this.pointA) == null || _this$pointA2.remove();
|
|
35073
|
-
(_this$pointB2 = this.pointB) == null || _this$pointB2.remove();
|
|
35074
|
-
if (this.sinusoid) {
|
|
35075
|
-
this.sinusoid.remove();
|
|
35076
|
-
this.sinusoid = null;
|
|
35077
|
-
}
|
|
35078
|
-
};
|
|
35079
|
-
this.addCircleControls = () => {
|
|
35080
|
-
const graphie = this.graphie;
|
|
35081
|
-
const minSnap = _.min(graphie.snap);
|
|
35082
|
-
const circle = this.circle = graphie == null ? void 0 : graphie.addCircleGraph({
|
|
35083
|
-
// @ts-expect-error - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'.
|
|
35084
|
-
center: this.props.graph.center || [0, 0],
|
|
35085
|
-
// @ts-expect-error - TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
35086
|
-
radius: this.props.graph.radius || _.min(this.props.step),
|
|
35087
|
-
snapX: graphie.snap[0],
|
|
35088
|
-
snapY: graphie.snap[1],
|
|
35089
|
-
minRadius: minSnap,
|
|
35090
|
-
snapRadius: minSnap
|
|
35091
|
-
});
|
|
35092
|
-
$(circle).on("move", () => {
|
|
35093
|
-
const graph = _.extend({}, this.props.graph, {
|
|
35094
|
-
center: circle.center,
|
|
35095
|
-
radius: circle.radius
|
|
35096
|
-
});
|
|
35097
|
-
this.onChange({
|
|
35098
|
-
graph: graph
|
|
35099
|
-
});
|
|
35100
|
-
});
|
|
35101
|
-
};
|
|
35102
|
-
this.removeCircleControls = () => {
|
|
35103
|
-
var _this$circle;
|
|
35104
|
-
(_this$circle = this.circle) == null || _this$circle.remove();
|
|
35105
|
-
};
|
|
35106
|
-
this.addLinearSystemControls = () => {
|
|
35107
|
-
const graphie = this.graphie;
|
|
35108
|
-
const coords = InteractiveGraph.getLinearSystemCoords(this.props.graph, this.props);
|
|
35109
|
-
const segmentColors = [KhanColors.INTERACTIVE, KhanColors.GREEN];
|
|
35110
|
-
const points = this.points = _.map(coords, (segmentCoords, segmentIndex) => {
|
|
35111
|
-
// NOTE(jeresig): This code attempts to access the variable
|
|
35112
|
-
// while it's still being defined, it should be refactored!
|
|
35113
|
-
// The callbacks appear to be executed synchronously, which
|
|
35114
|
-
// causes this issue.
|
|
35115
|
-
const segmentPoints = [];
|
|
35116
|
-
for (let i = 0; i < segmentCoords.length; i += 1) {
|
|
35117
|
-
const coord = segmentCoords[i];
|
|
35118
|
-
segmentPoints.push(Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35119
|
-
coord: coord,
|
|
35120
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35121
|
-
const otherSegment = segmentPoints[1 - i];
|
|
35122
|
-
if (!otherSegment) {
|
|
35123
|
-
// segment hasn't been defined yet
|
|
35124
|
-
// because we're still creating them
|
|
35125
|
-
return;
|
|
35126
|
-
}
|
|
35127
|
-
return !point.equal(coord, otherSegment.coord());
|
|
35128
|
-
}],
|
|
35129
|
-
onMove: () => {
|
|
35130
|
-
const graph = _.extend({}, this.props.graph, {
|
|
35131
|
-
coords: _.map(this.points, segment => _.invoke(segment, "coord"))
|
|
35132
|
-
});
|
|
35133
|
-
this.onChange({
|
|
35134
|
-
graph: graph
|
|
35135
|
-
});
|
|
35136
|
-
},
|
|
35137
|
-
normalStyle: {
|
|
35138
|
-
fill: segmentColors[segmentIndex]
|
|
35139
|
-
},
|
|
35140
|
-
highlightStyle: {
|
|
35141
|
-
fill: segmentColors[segmentIndex]
|
|
35142
|
-
}
|
|
35143
|
-
}));
|
|
35144
|
-
}
|
|
35145
|
-
return segmentPoints;
|
|
35146
|
-
});
|
|
35147
|
-
this.lines = _.map(points, (segmentPoints, segmentIndex) => {
|
|
35148
|
-
return Interactive2.addMovableLine(graphie, {
|
|
35149
|
-
points: segmentPoints,
|
|
35150
|
-
static: true,
|
|
35151
|
-
extendLine: true,
|
|
35152
|
-
normalStyle: {
|
|
35153
|
-
stroke: segmentColors[segmentIndex]
|
|
35154
|
-
}
|
|
35155
|
-
});
|
|
35156
|
-
});
|
|
35157
|
-
};
|
|
35158
|
-
this.removeLinearSystemControls = () => {
|
|
35159
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
35160
|
-
_.invoke(this.lines, "remove");
|
|
35161
|
-
_.map(this.points, segment => _.invoke(segment, "remove"));
|
|
35162
|
-
};
|
|
35163
|
-
this.isCoordInTrash = coord => {
|
|
35164
|
-
if (this.props.apiOptions.isMobile) {
|
|
35165
|
-
return false;
|
|
35166
|
-
}
|
|
35167
|
-
const graphie = this.graphie;
|
|
35168
|
-
const screenPoint = graphie == null ? void 0 : graphie.scalePoint(coord);
|
|
35169
|
-
return screenPoint[0] >= graphie.xpixels - 40 && screenPoint[1] >= graphie.ypixels - 40;
|
|
35170
|
-
};
|
|
35171
|
-
this.createPointForPointsType = (coord, i) => {
|
|
35172
|
-
const self = this;
|
|
35173
|
-
const remove = () => {
|
|
35174
|
-
self.points = _.filter(self.points, function (pt) {
|
|
35175
|
-
return pt !== point$1;
|
|
35176
|
-
});
|
|
35177
|
-
// update the correct answer box
|
|
35178
|
-
self.updateCoordsFromPoints();
|
|
35179
|
-
|
|
35180
|
-
// remove this movablePoint from graphie.
|
|
35181
|
-
// we wait to do this until we're not inside of
|
|
35182
|
-
// said point's onMoveEnd method so its state is
|
|
35183
|
-
// consistent throughout this method call
|
|
35184
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35185
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35186
|
-
setTimeout(point$1.remove.bind(point$1), 0); // eslint-disable-line no-restricted-syntax
|
|
35187
|
-
};
|
|
35188
|
-
|
|
35189
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35190
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35191
|
-
// to be executed synchronously, which causes this issue.
|
|
35192
|
-
let point$1 = null;
|
|
35193
|
-
point$1 = Interactive2.addMaybeMobileMovablePoint(this, _extends({
|
|
35194
|
-
coord: coord,
|
|
35195
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), function (coord) {
|
|
35196
|
-
// TODO(jack): There ought to be a
|
|
35197
|
-
// MovablePoint.constraints.avoid
|
|
35198
|
-
// default that lets you do things like this
|
|
35199
|
-
return _.all(self.points, function (pt) {
|
|
35200
|
-
return point$1 === pt || !point.equal(coord, pt.coord());
|
|
35201
|
-
});
|
|
35202
|
-
}],
|
|
35203
|
-
onMoveStart: function () {
|
|
35204
|
-
if (self.isClickToAddPoints()) {
|
|
35205
|
-
self.setTrashCanVisibility(1);
|
|
35206
|
-
}
|
|
35207
|
-
},
|
|
35208
|
-
onMove: self.updateCoordsFromPoints,
|
|
35209
|
-
onMoveEnd: function (coord) {
|
|
35210
|
-
if (self.isClickToAddPoints()) {
|
|
35211
|
-
if (self.isCoordInTrash(coord)) {
|
|
35212
|
-
remove();
|
|
35213
|
-
}
|
|
35214
|
-
// In case we mouseup'd off the graphie and that
|
|
35215
|
-
// stopped the move (in which case, we might not
|
|
35216
|
-
// be in isCoordInTrash()
|
|
35217
|
-
self.setTrashCanVisibility(0.5);
|
|
35218
|
-
}
|
|
35219
|
-
}
|
|
35220
|
-
}, this.props.apiOptions.isMobile && self.isClickToAddPoints() ? {
|
|
35221
|
-
onRemove: remove
|
|
35222
|
-
} : {}));
|
|
35223
|
-
return point$1;
|
|
35224
|
-
};
|
|
35225
|
-
this.removePoint = point => {
|
|
35226
|
-
let index = null;
|
|
35227
|
-
this.points = _.filter(this.points, function (pt, i) {
|
|
35228
|
-
if (pt === point) {
|
|
35229
|
-
// @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'null'.
|
|
35230
|
-
index = i;
|
|
35231
|
-
return false;
|
|
35232
|
-
}
|
|
35233
|
-
return true;
|
|
35234
|
-
});
|
|
35235
|
-
return index;
|
|
35236
|
-
};
|
|
35237
|
-
this.createPointForPolygonType = (coord, i) => {
|
|
35238
|
-
if (this.props.graph.type !== "polygon") {
|
|
35239
|
-
throw makeInvalidTypeError("createPointForPolygonType", "polygon");
|
|
35240
|
-
}
|
|
35241
|
-
|
|
35242
|
-
// TODO(alex): check against "grid" instead, use constants
|
|
35243
|
-
const snapToGrid = !_.contains(["angles", "sides"], this.props.graph.snapTo);
|
|
35244
|
-
|
|
35245
|
-
// Index relative to current point -> absolute index
|
|
35246
|
-
// NOTE: This does not work when isClickToAddPoints() == true,
|
|
35247
|
-
// as `i` can be changed by dragging a point to the trash
|
|
35248
|
-
// Currently this function is only called when !isClickToAddPoints()
|
|
35249
|
-
const rel = j => {
|
|
35250
|
-
return (i + j + this.points.length) % this.points.length;
|
|
35251
|
-
};
|
|
35252
|
-
const remove = () => {
|
|
35253
|
-
var _this$polygon2;
|
|
35254
|
-
// remove this point from points
|
|
35255
|
-
const index = this.removePoint(point$1);
|
|
35256
|
-
if ((_this$polygon2 = this.polygon) != null && _this$polygon2.closed()) {
|
|
35257
|
-
var _this$polygon3;
|
|
35258
|
-
// We should be checking if this.points is defined before rotating them.
|
|
35259
|
-
// @ts-expect-error - TS4104 - The type 'readonly any[]' is 'readonly' and cannot be assigned to the mutable type 'any[]'. | TS2345 - Argument of type 'number | null | undefined' is not assignable to parameter of type 'number | undefined'.
|
|
35260
|
-
this.points = rotate(this.points, index);
|
|
35261
|
-
(_this$polygon3 = this.polygon) == null || _this$polygon3.update({
|
|
35262
|
-
closed: false
|
|
35263
|
-
});
|
|
35264
|
-
}
|
|
35265
|
-
this.updatePolygon();
|
|
35266
|
-
// the polygon is now unclosed, so we need to
|
|
35267
|
-
// remove any points props
|
|
35268
|
-
this.clearCoords();
|
|
35269
|
-
|
|
35270
|
-
// remove this movablePoint from graphie.
|
|
35271
|
-
// wait to do this until we're not inside of
|
|
35272
|
-
// said point's onMoveEnd method so state is
|
|
35273
|
-
// consistent throughout the method call
|
|
35274
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35275
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35276
|
-
setTimeout(point$1.remove.bind(point$1), 0);
|
|
35277
|
-
};
|
|
35278
|
-
const setInitialMove = debounce$2((point, bool) => {
|
|
35279
|
-
point.state.isInitialMove = bool;
|
|
35280
|
-
}, 1000);
|
|
35281
|
-
const onMoveEndHandler = coord => {
|
|
35282
|
-
// If the user clicked and dragged a point over endpoint,
|
|
35283
|
-
// join the them
|
|
35284
|
-
const overlappedPoints = this.points.length > 1 && (point$1 === this.points[0] && point.equal(coord, _.last(this.points).coord()) || point$1 === _.last(this.points) && point.equal(coord, this.points[0].coord()));
|
|
35285
|
-
if (this.isClickToAddPoints()) {
|
|
35286
|
-
if (this.isCoordInTrash(coord)) {
|
|
35287
|
-
remove();
|
|
35288
|
-
} else if (overlappedPoints) {
|
|
35289
|
-
const pointToRemove = this.points.pop();
|
|
35290
|
-
if (this.points.length > 2) {
|
|
35291
|
-
var _this$polygon4;
|
|
35292
|
-
(_this$polygon4 = this.polygon) == null || _this$polygon4.update({
|
|
35293
|
-
closed: true
|
|
35294
|
-
});
|
|
35295
|
-
this.updateCoordsFromPoints();
|
|
35296
|
-
} else {
|
|
35297
|
-
var _this$polygon5;
|
|
35298
|
-
(_this$polygon5 = this.polygon) == null || _this$polygon5.update({
|
|
35299
|
-
closed: false
|
|
35300
|
-
});
|
|
35301
|
-
this.clearCoords();
|
|
35302
|
-
}
|
|
35303
|
-
this.updatePolygon();
|
|
35304
|
-
// remove this movablePoint from graphie.
|
|
35305
|
-
// wait to do this until we're not inside of
|
|
35306
|
-
// said point's onMoveEnd method so state is
|
|
35307
|
-
// consistent throughout the method call
|
|
35308
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35309
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35310
|
-
setTimeout(pointToRemove.remove.bind(pointToRemove), 0);
|
|
35311
|
-
} else {
|
|
35312
|
-
// If the user clicked and dragged a point over any other
|
|
35313
|
-
// existing point, fix shape
|
|
35314
|
-
const shouldRemove = _.any(this.points, function (pt) {
|
|
35315
|
-
return pt !== point$1 && point.equal(pt.coord(), coord);
|
|
35316
|
-
});
|
|
35317
|
-
if (shouldRemove) {
|
|
35318
|
-
var _this$polygon7;
|
|
35319
|
-
this.removePoint(point$1);
|
|
35320
|
-
if (this.points.length < 3) {
|
|
35321
|
-
var _this$polygon6;
|
|
35322
|
-
(_this$polygon6 = this.polygon) == null || _this$polygon6.update({
|
|
35323
|
-
closed: false
|
|
35324
|
-
});
|
|
35325
|
-
this.clearCoords();
|
|
35326
|
-
} else if ((_this$polygon7 = this.polygon) != null && _this$polygon7.closed()) {
|
|
35327
|
-
this.updateCoordsFromPoints();
|
|
35328
|
-
}
|
|
35329
|
-
this.updatePolygon();
|
|
35330
|
-
// remove this movablePoint from graphie.
|
|
35331
|
-
// wait to do this until we're not inside
|
|
35332
|
-
// said point's onMoveEnd method so state
|
|
35333
|
-
// is consistent throughout the method call
|
|
35334
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35335
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35336
|
-
setTimeout(point$1.remove.bind(point$1), 0);
|
|
35337
|
-
} else {
|
|
35338
|
-
var _this$polygon8;
|
|
35339
|
-
// If this was
|
|
35340
|
-
// * a click on the first or last point
|
|
35341
|
-
// * and not a deletion
|
|
35342
|
-
// * and not a drag,
|
|
35343
|
-
// * and not a creation of a new point
|
|
35344
|
-
// (see !point.state.isInitialMove, below),
|
|
35345
|
-
// * and our polygon is not closed,
|
|
35346
|
-
// * and we can close it (we need at least the same
|
|
35347
|
-
// number of points as coordinates asked for in the
|
|
35348
|
-
// question),
|
|
35349
|
-
// then close it
|
|
35350
|
-
const isFirstOrLastPoint = point$1 === _.head(this.points) || point$1 === _.last(this.points);
|
|
35351
|
-
const pointNotMoved = !point$1.hasMoved();
|
|
35352
|
-
// NOTE (jsatk): Currently different on mobile versus
|
|
35353
|
-
// destkop
|
|
35354
|
-
const notANewPoint = !point$1.state.isInitialMove;
|
|
35355
|
-
const polygonNotClosed = !((_this$polygon8 = this.polygon) != null && _this$polygon8.closed());
|
|
35356
|
-
const allPointsPlotted = this.points.length > 2;
|
|
35357
|
-
const canCloseUnclosedPolygon = isFirstOrLastPoint && pointNotMoved && notANewPoint && polygonNotClosed && allPointsPlotted;
|
|
35358
|
-
if (canCloseUnclosedPolygon) {
|
|
35359
|
-
var _this$polygon9;
|
|
35360
|
-
(_this$polygon9 = this.polygon) == null || _this$polygon9.update({
|
|
35361
|
-
closed: true
|
|
35362
|
-
});
|
|
35363
|
-
this.updatePolygon();
|
|
35364
|
-
|
|
35365
|
-
// We finally have a closed polygon, so save our
|
|
35366
|
-
// points to props
|
|
35367
|
-
this.updateCoordsFromPoints();
|
|
35368
|
-
}
|
|
35369
|
-
}
|
|
35370
|
-
}
|
|
35371
|
-
|
|
35372
|
-
// In case we mouseup'd off the graphie and that
|
|
35373
|
-
// stopped the move
|
|
35374
|
-
this.setTrashCanVisibility(0.5);
|
|
35375
|
-
}
|
|
35376
|
-
setInitialMove(point$1, false);
|
|
35377
|
-
};
|
|
35378
|
-
const graphConstraint = coord => {
|
|
35379
|
-
// These constraints are all relative to the other points, so if
|
|
35380
|
-
// we're creating the initial points and haven't added any others
|
|
35381
|
-
// to the graph, we can't enforce them.
|
|
35382
|
-
if (this.points == null || this.points.length === 0) {
|
|
35383
|
-
return true;
|
|
35384
|
-
}
|
|
35385
|
-
const coords = _.invoke(this.points, "coord");
|
|
35386
|
-
coords[i] = coord;
|
|
35387
|
-
|
|
35388
|
-
// Check for invalid positioning, but only if we aren't adding
|
|
35389
|
-
// points one click at a time, since those added points could
|
|
35390
|
-
// have already violated these constraints
|
|
35391
|
-
if (!this.isClickToAddPoints()) {
|
|
35392
|
-
// Polygons can't have consecutive collinear points
|
|
35393
|
-
if (collinear(coords[rel(-2)], coords[rel(-1)], coords[i]) || collinear(coords[rel(-1)], coords[i], coords[rel(1)]) || collinear(coords[i], coords[rel(1)], coords[rel(2)])) {
|
|
35394
|
-
return false;
|
|
35395
|
-
}
|
|
35396
|
-
const segments = _.zip(coords, rotate(coords));
|
|
35397
|
-
if (this.points.length > 3) {
|
|
35398
|
-
// Constrain to simple (non self-intersecting) polygon by
|
|
35399
|
-
// testing whether adjacent segments intersect any others
|
|
35400
|
-
for (let j = -1; j <= 0; j++) {
|
|
35401
|
-
const segment = segments[rel(j)];
|
|
35402
|
-
const others = _.without(segments, segment, segments[rel(j - 1)], segments[rel(j + 1)]);
|
|
35403
|
-
for (let k = 0; k < others.length; k++) {
|
|
35404
|
-
const other = others[k];
|
|
35405
|
-
// @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Line'.
|
|
35406
|
-
if (intersects(segment, other)) {
|
|
35407
|
-
return false;
|
|
35408
|
-
}
|
|
35409
|
-
}
|
|
35410
|
-
}
|
|
35411
|
-
}
|
|
35412
|
-
}
|
|
35413
|
-
if (
|
|
35414
|
-
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
35415
|
-
this.props.graph.snapTo === "angles" && this.points.length > 2) {
|
|
35416
|
-
var _this$graphie5;
|
|
35417
|
-
// Snap to whole degree interior angles
|
|
35418
|
-
|
|
35419
|
-
const angles = _.map(angleMeasures(coords), function (rad) {
|
|
35420
|
-
return rad * 180 / Math.PI;
|
|
35421
|
-
});
|
|
35422
|
-
_.each([-1, 1], function (j) {
|
|
35423
|
-
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
35424
|
-
});
|
|
35425
|
-
const getAngle = function getAngle(a, vertex, b) {
|
|
35426
|
-
const angle = GraphUtils.findAngleDeprecated(coords[rel(a)], coords[rel(b)], coords[rel(vertex)]);
|
|
35427
|
-
return (angle + 360) % 360;
|
|
35428
|
-
};
|
|
35429
|
-
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
35430
|
-
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
35431
|
-
|
|
35432
|
-
// Avoid degenerate triangles
|
|
35433
|
-
if (_.any(innerAngles, function (angle) {
|
|
35434
|
-
return leq(angle, 1);
|
|
35435
|
-
})) {
|
|
35436
|
-
return false;
|
|
35437
|
-
}
|
|
35438
|
-
const knownSide = magnitude(
|
|
35439
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
35440
|
-
vector(coords[rel(-1)], coords[rel(1)]));
|
|
35441
|
-
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
35442
|
-
|
|
35443
|
-
// Solve for side by using the law of sines
|
|
35444
|
-
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
35445
|
-
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
35446
|
-
const offset = polar$2(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
35447
|
-
return (_this$graphie5 = this.graphie) == null ? void 0 : _this$graphie5.addPoints(coords[rel(-1)], offset);
|
|
35448
|
-
}
|
|
35449
|
-
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
35450
|
-
if (this.props.graph.snapTo === "sides" && this.points.length > 1) {
|
|
35451
|
-
var _this$graphie6;
|
|
35452
|
-
// Snap to whole unit side measures
|
|
35453
|
-
|
|
35454
|
-
const sides = _.map([[coords[rel(-1)], coords[i]], [coords[i], coords[rel(1)]], [coords[rel(-1)], coords[rel(1)]]], function (coords) {
|
|
35455
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
35456
|
-
return magnitude(vector(...coords));
|
|
35457
|
-
});
|
|
35458
|
-
_.each([0, 1], function (j) {
|
|
35459
|
-
sides[j] = Math.round(sides[j]);
|
|
35460
|
-
});
|
|
35461
|
-
|
|
35462
|
-
// Avoid degenerate triangles
|
|
35463
|
-
if (leq(sides[1] + sides[2], sides[0]) || leq(sides[0] + sides[2], sides[1]) || leq(sides[0] + sides[1], sides[2])) {
|
|
35464
|
-
return false;
|
|
35465
|
-
}
|
|
35466
|
-
|
|
35467
|
-
// Solve for angle by using the law of cosines
|
|
35468
|
-
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
35469
|
-
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
35470
|
-
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
35471
|
-
const offset = polar$2(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
35472
|
-
return (_this$graphie6 = this.graphie) == null ? void 0 : _this$graphie6.addPoints(coords[rel(-1)], offset);
|
|
35473
|
-
}
|
|
35474
|
-
// Snap to grid (already done)
|
|
35475
|
-
return true;
|
|
35476
|
-
};
|
|
35477
|
-
const point$1 = Interactive2.addMaybeMobileMovablePoint(this, _extends({
|
|
35478
|
-
coord: coord,
|
|
35479
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), snapToGrid ? Interactive2.MovablePoint.constraints.snap() : null, graphConstraint],
|
|
35480
|
-
onMoveStart: () => {
|
|
35481
|
-
if (this.isClickToAddPoints()) {
|
|
35482
|
-
this.setTrashCanVisibility(1);
|
|
35483
|
-
}
|
|
35484
|
-
},
|
|
35485
|
-
onMove: () => {
|
|
35486
|
-
var _this$polygon10;
|
|
35487
|
-
if ((_this$polygon10 = this.polygon) != null && _this$polygon10.closed()) {
|
|
35488
|
-
this.updateCoordsFromPoints();
|
|
35489
|
-
}
|
|
35490
|
-
},
|
|
35491
|
-
onMoveEnd: onMoveEndHandler
|
|
35492
|
-
}, this.props.apiOptions.isMobile && this.isClickToAddPoints() ? {
|
|
35493
|
-
onRemove: remove
|
|
35494
|
-
} : {}));
|
|
35495
|
-
point$1.state.isInitialMove = true;
|
|
35496
|
-
return point$1;
|
|
35497
|
-
};
|
|
35498
|
-
this.updateCoordsFromPoints = () => {
|
|
35499
|
-
const graph = _.extend({}, this.props.graph, {
|
|
35500
|
-
// Handle old movable points with .coord, or
|
|
35501
|
-
// Interactive2.MovablePoint's with .coord()
|
|
35502
|
-
coords: _.map(this.points, function (point) {
|
|
35503
|
-
return _.result(point, "coord");
|
|
35504
|
-
})
|
|
35505
|
-
});
|
|
35506
|
-
this.onChange({
|
|
35507
|
-
graph: graph
|
|
35508
|
-
});
|
|
35509
|
-
};
|
|
35510
|
-
this.clearCoords = () => {
|
|
35511
|
-
const graph = _.extend({}, this.props.graph, {
|
|
35512
|
-
coords: null
|
|
35513
|
-
});
|
|
35514
|
-
this.onChange({
|
|
35515
|
-
graph: graph
|
|
35516
|
-
});
|
|
35517
|
-
};
|
|
35518
|
-
this.onChange = data => {
|
|
35519
|
-
this.props.onChange(data);
|
|
35520
|
-
this.props.trackInteraction();
|
|
35521
|
-
};
|
|
35522
|
-
this.addPointControls = () => {
|
|
35523
|
-
if (this.props.graph.type !== "point") {
|
|
35524
|
-
throw makeInvalidTypeError("addPointControls", "point");
|
|
35525
|
-
}
|
|
35526
|
-
const coords = InteractiveGraph.getPointCoords(this.props.graph, this.props);
|
|
35527
|
-
// Clear out our old points so that newly added points don't
|
|
35528
|
-
// "collide" with them and reposition when being added
|
|
35529
|
-
// Without this, when added, each point checks whether it is on top
|
|
35530
|
-
// of a point in this.points, which (a) shouldn't matter since
|
|
35531
|
-
// we're clearing out this.points anyways, and (b) can cause problems
|
|
35532
|
-
// if each of this.points is a MovablePoint instead of an
|
|
35533
|
-
// Interactive2.MovablePoint, since one has a .coord and the other
|
|
35534
|
-
// has .coord()
|
|
35535
|
-
// TODO(jack): Figure out a better way to do this
|
|
35536
|
-
this.points = [];
|
|
35537
|
-
this.points = _.map(coords, this.createPointForPointsType, this);
|
|
35538
|
-
};
|
|
35539
|
-
this.removePointControls = () => {
|
|
35540
|
-
_.invoke(this.points, "remove");
|
|
35541
|
-
};
|
|
35542
|
-
this.addSegmentControls = () => {
|
|
35543
|
-
if (this.props.graph.type !== "segment") {
|
|
35544
|
-
throw makeInvalidTypeError("addSegmentControls", "segment");
|
|
35545
|
-
}
|
|
35546
|
-
const self = this;
|
|
35547
|
-
const graphie = this.graphie;
|
|
35548
|
-
const coords = InteractiveGraph.getSegmentCoords(this.props.graph, this.props);
|
|
35549
|
-
const createPoint = options => Interactive2.addMaybeMobileMovablePoint(this, options);
|
|
35550
|
-
this.points = [];
|
|
35551
|
-
this.lines = _.map(coords, function (segment, i) {
|
|
35552
|
-
const updateCoordProps = function updateCoordProps() {
|
|
35553
|
-
const graph = _.extend({}, self.props.graph, {
|
|
35554
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
35555
|
-
coords: _.invoke(self.lines, "coords")
|
|
35556
|
-
});
|
|
35557
|
-
self.onChange({
|
|
35558
|
-
graph: graph
|
|
35559
|
-
});
|
|
35560
|
-
};
|
|
35561
|
-
|
|
35562
|
-
// NOTE(jeresig): This code attempts to access the variable
|
|
35563
|
-
// while it's still being defined, it should be refactored!
|
|
35564
|
-
// The callbacks appear to be executed synchronously, which
|
|
35565
|
-
// causes this issue.
|
|
35566
|
-
const points = [];
|
|
35567
|
-
for (let _i = 0; _i < segment.length; _i += 1) {
|
|
35568
|
-
const coord = segment[_i];
|
|
35569
|
-
points.push(createPoint({
|
|
35570
|
-
coord: coord,
|
|
35571
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35572
|
-
const otherPoint = points[1 - _i];
|
|
35573
|
-
if (!otherPoint) {
|
|
35574
|
-
// point hasn't been defined yet
|
|
35575
|
-
// because we're still creating them
|
|
35576
|
-
// We return false as the points aren't
|
|
35577
|
-
// going to be in the same position at
|
|
35578
|
-
// this point.
|
|
35579
|
-
return false;
|
|
35580
|
-
}
|
|
35581
|
-
return !point.equal(coord, otherPoint.coord());
|
|
35582
|
-
}],
|
|
35583
|
-
onMove: updateCoordProps
|
|
35584
|
-
}));
|
|
35585
|
-
}
|
|
35586
|
-
self.points = self.points.concat(points);
|
|
35587
|
-
const line = Interactive2.addMovableLine(graphie, {
|
|
35588
|
-
points: points,
|
|
35589
|
-
static: false,
|
|
35590
|
-
constraints: [
|
|
35591
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
35592
|
-
Interactive2.MovableLine.constraints.bound(),
|
|
35593
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
35594
|
-
Interactive2.MovableLine.constraints.snap()],
|
|
35595
|
-
onMove: [
|
|
35596
|
-
// @ts-expect-error - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
35597
|
-
Interactive2.MovableLine.onMove.updatePoints, updateCoordProps],
|
|
35598
|
-
normalStyle: _extends({
|
|
35599
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
35600
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE
|
|
35601
|
-
}, this._lineStroke()),
|
|
35602
|
-
highlightStyle: _extends({
|
|
35603
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
35604
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTING
|
|
35605
|
-
}, this._lineStroke())
|
|
35606
|
-
});
|
|
35607
|
-
_.invoke(points, "toFront");
|
|
35608
|
-
return line;
|
|
35609
|
-
}, this);
|
|
35610
|
-
};
|
|
35611
|
-
this.removeSegmentControls = () => {
|
|
35612
|
-
_.invoke(this.points, "remove");
|
|
35613
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
35614
|
-
_.invoke(this.lines, "remove");
|
|
35615
|
-
};
|
|
35616
|
-
this.addRayControls = () => {
|
|
35617
|
-
this.addLine("ray");
|
|
35618
|
-
};
|
|
35619
|
-
this.removeRayControls = () => {
|
|
35620
|
-
this.removeLine();
|
|
35621
|
-
};
|
|
35622
|
-
this.addPolygonControls = () => {
|
|
35623
|
-
this.polygon = null;
|
|
35624
|
-
const coords = InteractiveGraph.getPolygonCoords(this.props.graph, this.props);
|
|
35625
|
-
// Clear out our old points so that newly added points don't
|
|
35626
|
-
// "collide", as in `addPointControls`
|
|
35627
|
-
this.points = [];
|
|
35628
|
-
this.points = _.map(coords, this.createPointForPolygonType, this);
|
|
35629
|
-
this.updatePolygon();
|
|
35630
|
-
};
|
|
35631
|
-
this.updatePolygon = () => {
|
|
35632
|
-
if (this.props.graph.type !== "polygon") {
|
|
35633
|
-
throw makeInvalidTypeError("updatePolygon", "polygon");
|
|
35634
|
-
}
|
|
35635
|
-
let closed;
|
|
35636
|
-
if (this.polygon) {
|
|
35637
|
-
closed = this.polygon.closed();
|
|
35638
|
-
} else if (this.points.length >= 3) {
|
|
35639
|
-
closed = true;
|
|
35640
|
-
} else {
|
|
35641
|
-
// There will only be fewer than 3 points in click-to-add-vertices
|
|
35642
|
-
// mode, so we don't need to explicitly check for that here.
|
|
35643
|
-
closed = false;
|
|
35644
|
-
}
|
|
35645
|
-
const graphie = this.graphie;
|
|
35646
|
-
const n = this.points.length;
|
|
35647
|
-
|
|
35648
|
-
// TODO(alex): check against "grid" instead, use constants
|
|
35649
|
-
const snapToGrid = !_.contains(["angles", "sides"], this.props.graph.snapTo);
|
|
35650
|
-
const angleLabels = _.times(n, function (i) {
|
|
35651
|
-
if (
|
|
35652
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
35653
|
-
!this.props.graph.showAngles || !closed && (i === 0 || i === n - 1)) {
|
|
35654
|
-
return "";
|
|
35655
|
-
}
|
|
35656
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
35657
|
-
if (this.props.graph.snapTo === "angles") {
|
|
35658
|
-
return "$deg0";
|
|
35659
|
-
}
|
|
35660
|
-
return "$deg1";
|
|
35661
|
-
}, this);
|
|
35662
|
-
const showRightAngleMarkers = _.times(n, function (i) {
|
|
35663
|
-
return closed || i !== 0 && i !== n - 1;
|
|
35664
|
-
}, this);
|
|
35665
|
-
const numArcs = _.times(n, function (i) {
|
|
35666
|
-
if (
|
|
35667
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
35668
|
-
this.props.graph.showAngles && (closed || i !== 0 && i !== n - 1)) {
|
|
35669
|
-
return 1;
|
|
35670
|
-
}
|
|
35671
|
-
return 0;
|
|
35672
|
-
}, this);
|
|
35673
|
-
const sideLabels = _.times(n, function (i) {
|
|
35674
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
35675
|
-
if (!this.props.graph.showSides || !closed && i === n - 1) {
|
|
35676
|
-
return "";
|
|
35677
|
-
}
|
|
35678
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
35679
|
-
if (this.props.graph.snapTo === "sides") {
|
|
35680
|
-
return "$len0";
|
|
35681
|
-
}
|
|
35682
|
-
return "$len1";
|
|
35683
|
-
}, this);
|
|
35684
|
-
if (this.polygon == null) {
|
|
35685
|
-
const self = this;
|
|
35686
|
-
self.polygon = Interactive2.addMovablePolygon(graphie, {
|
|
35687
|
-
constraints: [
|
|
35688
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
35689
|
-
Interactive2.MovablePolygon.constraints.bound(), snapToGrid ?
|
|
35690
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
35691
|
-
Interactive2.MovablePolygon.constraints.snap() : null],
|
|
35692
|
-
closed: closed,
|
|
35693
|
-
points: self.points,
|
|
35694
|
-
angleLabels: angleLabels,
|
|
35695
|
-
showRightAngleMarkers: showRightAngleMarkers,
|
|
35696
|
-
numArcs: numArcs,
|
|
35697
|
-
sideLabels: sideLabels,
|
|
35698
|
-
onMove: [
|
|
35699
|
-
// @ts-expect-error - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
35700
|
-
Interactive2.MovablePolygon.onMove.updatePoints, function () {
|
|
35701
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
35702
|
-
if (this.closed()) {
|
|
35703
|
-
self.updateCoordsFromPoints();
|
|
35704
|
-
}
|
|
35705
|
-
}],
|
|
35706
|
-
normalStyle: _extends({
|
|
35707
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE
|
|
35708
|
-
}, this._lineStroke())
|
|
35709
|
-
});
|
|
35710
|
-
} else {
|
|
35711
|
-
// We only need to pass in the properties that might've changed
|
|
35712
|
-
this.polygon.update({
|
|
35713
|
-
closed: closed,
|
|
35714
|
-
points: this.points,
|
|
35715
|
-
angleLabels: angleLabels,
|
|
35716
|
-
showRightAngleMarkers: showRightAngleMarkers,
|
|
35717
|
-
numArcs: numArcs,
|
|
35718
|
-
sideLabels: sideLabels
|
|
35719
|
-
});
|
|
35720
|
-
}
|
|
35721
|
-
};
|
|
35722
|
-
this.removePolygonControls = () => {
|
|
35723
|
-
var _this$polygon11;
|
|
35724
|
-
_.invoke(this.points, "remove");
|
|
35725
|
-
(_this$polygon11 = this.polygon) == null || _this$polygon11.remove();
|
|
35726
|
-
};
|
|
35727
|
-
this.addAngleControls = () => {
|
|
35728
|
-
if (this.props.graph.type !== "angle") {
|
|
35729
|
-
throw makeInvalidTypeError("addAngleControls", "angle");
|
|
35730
|
-
}
|
|
35731
|
-
const graph = this.props.graph;
|
|
35732
|
-
const graphie = this.graphie;
|
|
35733
|
-
const coords = InteractiveGraph.getAngleCoords(this.props.graph, this.props);
|
|
35734
|
-
|
|
35735
|
-
// The vertex snaps to the grid, but the rays don't...
|
|
35736
|
-
this.points = coords.map(function (coord, i) {
|
|
35737
|
-
return graphie == null ? void 0 : graphie.addMovablePoint(_.extend({
|
|
35738
|
-
coord: coord,
|
|
35739
|
-
normalStyle: {
|
|
35740
|
-
stroke: KhanColors.INTERACTIVE,
|
|
35741
|
-
fill: KhanColors.INTERACTIVE
|
|
35742
|
-
}
|
|
35743
|
-
}, i === 1 ? {
|
|
35744
|
-
snapX: graphie.snap[0],
|
|
35745
|
-
snapY: graphie.snap[1]
|
|
35746
|
-
} : {}));
|
|
35747
|
-
});
|
|
35748
|
-
|
|
35749
|
-
// ...they snap to whole-degree angles from the vertex.
|
|
35750
|
-
this.angle = graphie == null ? void 0 : graphie.addMovableAngle({
|
|
35751
|
-
points: this.points,
|
|
35752
|
-
snapDegrees: graph.snapDegrees || 1,
|
|
35753
|
-
snapOffsetDeg: graph.angleOffsetDeg || 0,
|
|
35754
|
-
angleLabel: graph.showAngles ? "$deg0" : "",
|
|
35755
|
-
pushOut: 2,
|
|
35756
|
-
allowReflex: defaultVal(graph.allowReflexAngles, true)
|
|
35757
|
-
});
|
|
35758
|
-
$(this.angle).on("move", () => {
|
|
35759
|
-
var _this$angle;
|
|
35760
|
-
this.onChange({
|
|
35761
|
-
graph: _extends({}, graph, {
|
|
35762
|
-
coords: (_this$angle = this.angle) == null ? void 0 : _this$angle.getClockwiseCoords()
|
|
35763
|
-
})
|
|
35764
|
-
});
|
|
35765
|
-
});
|
|
35766
|
-
};
|
|
35767
|
-
this.removeAngleControls = () => {
|
|
35768
|
-
var _this$angle2;
|
|
35769
|
-
_.invoke(this.points, "remove");
|
|
35770
|
-
(_this$angle2 = this.angle) == null || _this$angle2.remove();
|
|
35771
|
-
};
|
|
35772
|
-
this.toggleShowSides = () => {
|
|
35773
|
-
if (this.props.graph.type !== "polygon") {
|
|
35774
|
-
throw makeInvalidTypeError("toggleShowSides", "polygon");
|
|
35775
|
-
}
|
|
35776
|
-
const graph = _.extend({}, this.props.graph, {
|
|
35777
|
-
showSides: !this.props.graph.showSides
|
|
35778
|
-
});
|
|
35779
|
-
this.onChange({
|
|
35780
|
-
graph: graph
|
|
35781
|
-
});
|
|
35782
|
-
};
|
|
35783
|
-
this.focus = $.noop;
|
|
35784
|
-
}
|
|
35785
|
-
componentDidMount() {
|
|
35786
|
-
if (this.refs.graph) {
|
|
35787
|
-
// eslint-disable-next-line react/no-string-refs
|
|
35788
|
-
// @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
|
|
35789
|
-
this.setGraphie(this.refs.graph.graphie());
|
|
35790
|
-
}
|
|
35791
|
-
}
|
|
35792
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
35793
|
-
var _this$props$backgroun, _nextProps$background;
|
|
35794
|
-
if (this.isClickToAddPoints() !== this.isClickToAddPoints(nextProps)) {
|
|
35795
|
-
this.shouldResetGraphie = true;
|
|
35796
|
-
this.setState({
|
|
35797
|
-
shouldShowInstructions: _getShouldShowInstructions(nextProps)
|
|
35798
|
-
});
|
|
35799
|
-
}
|
|
35800
|
-
if (((_this$props$backgroun = this.props.backgroundImage) == null ? void 0 : _this$props$backgroun.url) !== ((_nextProps$background = nextProps.backgroundImage) == null ? void 0 : _nextProps$background.url) || this.props.backgroundImage !== nextProps.backgroundImage || this.props.containerSizeClass !== nextProps.containerSizeClass) {
|
|
35801
|
-
this.shouldResetGraphie = true;
|
|
35802
|
-
}
|
|
35803
|
-
}
|
|
35804
|
-
componentDidUpdate(prevProps, prevState) {
|
|
35805
|
-
// TODO(jeremy): This feels underspecified. For example, if the props
|
|
35806
|
-
// change the coords of a segment graph, but not the numSegments, we'd
|
|
35807
|
-
// not count that as an update.
|
|
35808
|
-
const oldType = prevProps.graph.type;
|
|
35809
|
-
const newType = this.props.graph.type;
|
|
35810
|
-
if (oldType !== newType || prevProps.graph.type === "angle" && this.props.graph.type === "angle" && this.areAngleGraphsEqual(prevProps.graph, this.props.graph) || prevProps.graph.type === "point" && this.props.graph.type === "point" && this.arePointGraphsEqual(prevProps.graph, this.props.graph) || prevProps.graph.type === "polygon" && this.props.graph.type === "polygon" && this.arePolygonGraphsEqual(prevProps.graph, this.props.graph) || prevProps.graph.type === "segment" && this.props.graph.type === "segment" && this.areSegmentGraphsEqual(prevProps.graph, this.props.graph)) {
|
|
35811
|
-
this["remove" + capitalize(oldType) + "Controls"]();
|
|
35812
|
-
this["add" + capitalize(newType) + "Controls"]();
|
|
35813
|
-
}
|
|
35814
|
-
if (this.shouldResetGraphie) {
|
|
35815
|
-
this.resetGraphie();
|
|
35816
|
-
}
|
|
35817
|
-
}
|
|
35818
|
-
areAngleGraphsEqual(prevGraph, currentGraph) {
|
|
35819
|
-
return prevGraph.allowReflexAngles !== currentGraph.allowReflexAngles || prevGraph.angleOffsetDeg !== currentGraph.angleOffsetDeg || prevGraph.snapDegrees !== currentGraph.snapDegrees;
|
|
35820
|
-
}
|
|
35821
|
-
arePointGraphsEqual(prevGraph, currentGraph) {
|
|
35822
|
-
return prevGraph.numPoints !== currentGraph.numPoints;
|
|
35823
|
-
}
|
|
35824
|
-
arePolygonGraphsEqual(prevGraph, currentGraph) {
|
|
35825
|
-
return prevGraph.numSides !== currentGraph.numSides || prevGraph.showAngles !== currentGraph.showAngles || prevGraph.showSides !== currentGraph.showSides || prevGraph.snapTo !== currentGraph.snapTo;
|
|
35826
|
-
}
|
|
35827
|
-
areSegmentGraphsEqual(prevGraph, currentGraph) {
|
|
35828
|
-
return prevGraph.numSegments !== currentGraph.numSegments;
|
|
35829
|
-
}
|
|
35830
|
-
getUserInput() {
|
|
35831
|
-
return InteractiveGraph.getUserInputFromProps(this.props);
|
|
35832
|
-
}
|
|
35833
|
-
render() {
|
|
35834
|
-
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
35835
|
-
const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
35836
|
-
const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
35837
|
-
let instructions;
|
|
35838
|
-
// isClickToAddPoints() only applies to points and polygons
|
|
35839
|
-
if (this.isClickToAddPoints() && this.state.shouldShowInstructions) {
|
|
35840
|
-
if (this.props.graph.type === "point") {
|
|
35841
|
-
instructions = this.context.strings.addPoints;
|
|
35842
|
-
} else if (this.props.graph.type === "polygon") {
|
|
35843
|
-
instructions = this.context.strings.addVertices;
|
|
35844
|
-
}
|
|
35845
|
-
} else {
|
|
35846
|
-
instructions = undefined;
|
|
35847
|
-
}
|
|
35848
|
-
let onMouseDown;
|
|
35849
|
-
if (this.isClickToAddPoints()) {
|
|
35850
|
-
onMouseDown = this.handleAddPointsMouseDown;
|
|
35851
|
-
}
|
|
35852
|
-
const isMobile = this.props.apiOptions.isMobile;
|
|
35853
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
35854
|
-
className: "perseus-widget " + "perseus-widget-interactive-graph",
|
|
35855
|
-
style: {
|
|
35856
|
-
width: box[0],
|
|
35857
|
-
height: box[1]
|
|
35858
|
-
}
|
|
35859
|
-
}, /*#__PURE__*/React.createElement(Graph, {
|
|
35860
|
-
instructions: instructions
|
|
35861
|
-
// eslint-disable-next-line react/no-string-refs
|
|
35862
|
-
,
|
|
35863
|
-
ref: "graph",
|
|
35864
|
-
box: box,
|
|
35865
|
-
labels: this.props.labels,
|
|
35866
|
-
range: this.props.range,
|
|
35867
|
-
step: isMobile ? Util.constrainedTickStepsFromTickSteps(this.props.step, this.props.range) : this.props.step,
|
|
35868
|
-
gridStep: gridStep,
|
|
35869
|
-
snapStep: snapStep,
|
|
35870
|
-
markings: this.props.markings,
|
|
35871
|
-
backgroundImage: this.props.backgroundImage,
|
|
35872
|
-
showProtractor: this.props.showProtractor,
|
|
35873
|
-
onMouseDown: onMouseDown,
|
|
35874
|
-
onGraphieUpdated: this.setGraphie,
|
|
35875
|
-
setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable,
|
|
35876
|
-
isMobile: isMobile
|
|
35877
|
-
}));
|
|
35878
|
-
}
|
|
35879
|
-
}
|
|
35880
|
-
LegacyInteractiveGraph.contextType = PerseusI18nContext;
|
|
35881
|
-
LegacyInteractiveGraph.defaultProps = {
|
|
35882
|
-
labels: ["x", "y"],
|
|
35883
|
-
range: [[-10, 10], [-10, 10]],
|
|
35884
|
-
step: [1, 1],
|
|
35885
|
-
backgroundImage: defaultBackgroundImage,
|
|
35886
|
-
markings: "graph",
|
|
35887
|
-
showTooltips: false,
|
|
35888
|
-
showProtractor: false,
|
|
35889
|
-
graph: {
|
|
35890
|
-
type: "linear"
|
|
35891
|
-
}
|
|
35892
|
-
};
|
|
35893
34940
|
class InteractiveGraph extends React.Component {
|
|
35894
34941
|
constructor(...args) {
|
|
35895
34942
|
super(...args);
|
|
35896
|
-
this.legacyGraphRef = /*#__PURE__*/React.createRef();
|
|
35897
34943
|
this.mafsRef = /*#__PURE__*/React.createRef();
|
|
35898
34944
|
}
|
|
35899
34945
|
getUserInput() {
|
|
35900
|
-
var _this$mafsRef$current
|
|
34946
|
+
var _this$mafsRef$current;
|
|
35901
34947
|
if ((_this$mafsRef$current = this.mafsRef.current) != null && _this$mafsRef$current.getUserInput) {
|
|
35902
34948
|
return this.mafsRef.current.getUserInput();
|
|
35903
34949
|
}
|
|
35904
|
-
if ((_this$legacyGraphRef$ = this.legacyGraphRef.current) != null && _this$legacyGraphRef$.getUserInput) {
|
|
35905
|
-
return this.legacyGraphRef.current.getUserInput();
|
|
35906
|
-
}
|
|
35907
34950
|
throw new PerseusError("Cannot getUserInput from a graph that has never rendered", Errors.NotAllowed);
|
|
35908
34951
|
}
|
|
35909
34952
|
getPromptJSON() {
|
|
@@ -35911,26 +34954,17 @@ class InteractiveGraph extends React.Component {
|
|
|
35911
34954
|
}
|
|
35912
34955
|
render() {
|
|
35913
34956
|
var _this$props$apiOption;
|
|
35914
|
-
|
|
35915
|
-
const
|
|
35916
|
-
|
|
35917
|
-
|
|
35918
|
-
|
|
35919
|
-
|
|
35920
|
-
|
|
35921
|
-
|
|
35922
|
-
|
|
35923
|
-
|
|
35924
|
-
|
|
35925
|
-
snapStep: snapStep,
|
|
35926
|
-
box: box,
|
|
35927
|
-
showTooltips: !!this.props.showTooltips,
|
|
35928
|
-
readOnly: (_this$props$apiOption3 = this.props.apiOptions) == null ? void 0 : _this$props$apiOption3.readOnly
|
|
35929
|
-
}));
|
|
35930
|
-
}
|
|
35931
|
-
return /*#__PURE__*/React.createElement(LegacyInteractiveGraph, _extends({
|
|
35932
|
-
ref: this.legacyGraphRef
|
|
35933
|
-
}, this.props));
|
|
34957
|
+
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
34958
|
+
const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
34959
|
+
const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
34960
|
+
return /*#__PURE__*/React.createElement(StatefulMafsGraph, _extends({}, this.props, {
|
|
34961
|
+
ref: this.mafsRef,
|
|
34962
|
+
gridStep: gridStep,
|
|
34963
|
+
snapStep: snapStep,
|
|
34964
|
+
box: box,
|
|
34965
|
+
showTooltips: !!this.props.showTooltips,
|
|
34966
|
+
readOnly: (_this$props$apiOption = this.props.apiOptions) == null ? void 0 : _this$props$apiOption.readOnly
|
|
34967
|
+
}));
|
|
35934
34968
|
}
|
|
35935
34969
|
|
|
35936
34970
|
/**
|
|
@@ -36107,8 +35141,32 @@ class InteractiveGraph extends React.Component {
|
|
|
36107
35141
|
}
|
|
36108
35142
|
static getEquationString(props) {
|
|
36109
35143
|
const type = props.graph.type;
|
|
36110
|
-
|
|
36111
|
-
|
|
35144
|
+
switch (type) {
|
|
35145
|
+
case "none":
|
|
35146
|
+
return InteractiveGraph.getNoneEquationString();
|
|
35147
|
+
case "linear":
|
|
35148
|
+
return InteractiveGraph.getLinearEquationString(props);
|
|
35149
|
+
case "quadratic":
|
|
35150
|
+
return InteractiveGraph.getQuadraticEquationString(props);
|
|
35151
|
+
case "sinusoid":
|
|
35152
|
+
return InteractiveGraph.getSinusoidEquationString(props);
|
|
35153
|
+
case "circle":
|
|
35154
|
+
return InteractiveGraph.getCircleEquationString(props);
|
|
35155
|
+
case "linear-system":
|
|
35156
|
+
return InteractiveGraph.getLinearSystemEquationString(props);
|
|
35157
|
+
case "point":
|
|
35158
|
+
return InteractiveGraph.getPointEquationString(props);
|
|
35159
|
+
case "segment":
|
|
35160
|
+
return InteractiveGraph.getSegmentEquationString(props);
|
|
35161
|
+
case "ray":
|
|
35162
|
+
return InteractiveGraph.getRayEquationString(props);
|
|
35163
|
+
case "polygon":
|
|
35164
|
+
return InteractiveGraph.getPolygonEquationString(props);
|
|
35165
|
+
case "angle":
|
|
35166
|
+
return InteractiveGraph.getAngleEquationString(props);
|
|
35167
|
+
default:
|
|
35168
|
+
throw new UnreachableCaseError(type);
|
|
35169
|
+
}
|
|
36112
35170
|
}
|
|
36113
35171
|
static pointsFromNormalized(props, coordsList, noSnap) {
|
|
36114
35172
|
// @ts-expect-error - TS2322 - Type 'number[][]' is not assignable to type 'readonly Coord[]'.
|
|
@@ -36246,7 +35304,7 @@ class InteractiveGraph extends React.Component {
|
|
|
36246
35304
|
}
|
|
36247
35305
|
}
|
|
36248
35306
|
|
|
36249
|
-
//
|
|
35307
|
+
// We don't need to change any of the original props for static mode
|
|
36250
35308
|
InteractiveGraph.defaultProps = {
|
|
36251
35309
|
labels: ["x", "y"],
|
|
36252
35310
|
range: [[-10, 10], [-10, 10]],
|
|
@@ -36259,28 +35317,6 @@ InteractiveGraph.defaultProps = {
|
|
|
36259
35317
|
type: "linear"
|
|
36260
35318
|
}
|
|
36261
35319
|
};
|
|
36262
|
-
function shouldUseMafs(mafsFlags, graph) {
|
|
36263
|
-
if (typeof mafsFlags === "boolean" || typeof mafsFlags === "undefined") {
|
|
36264
|
-
return false;
|
|
36265
|
-
}
|
|
36266
|
-
switch (graph.type) {
|
|
36267
|
-
case "none":
|
|
36268
|
-
return true;
|
|
36269
|
-
case "point":
|
|
36270
|
-
if (graph.numPoints === UNLIMITED) {
|
|
36271
|
-
return Boolean(mafsFlags["unlimited-point"]);
|
|
36272
|
-
}
|
|
36273
|
-
return Boolean(mafsFlags["point"]);
|
|
36274
|
-
case "polygon":
|
|
36275
|
-
if (graph.numSides === UNLIMITED) {
|
|
36276
|
-
return Boolean(mafsFlags["unlimited-polygon"]);
|
|
36277
|
-
}
|
|
36278
|
-
return Boolean(mafsFlags["polygon"]);
|
|
36279
|
-
default:
|
|
36280
|
-
return Boolean(mafsFlags[graph.type]);
|
|
36281
|
-
}
|
|
36282
|
-
}
|
|
36283
|
-
// We don't need to change any of the original props for static mode
|
|
36284
35320
|
const staticTransform$3 = _.identity;
|
|
36285
35321
|
var InteractiveGraph$1 = {
|
|
36286
35322
|
name: "interactive-graph",
|
|
@@ -36913,11 +35949,16 @@ class LabelImage extends React.Component {
|
|
|
36913
35949
|
});
|
|
36914
35950
|
}
|
|
36915
35951
|
activateMarker(index, opened) {
|
|
36916
|
-
var _this$props$analytics;
|
|
35952
|
+
var _this$props$analytics, _this$props$analytics2;
|
|
35953
|
+
// TODO(LEMS-2830): Remove analytics event in LEMS-2830 in favor of ti below.
|
|
36917
35954
|
(_this$props$analytics = this.props.analytics) == null || _this$props$analytics.onAnalyticsEvent({
|
|
36918
35955
|
type: "perseus:label-image:marker-interacted-with",
|
|
36919
35956
|
payload: null
|
|
36920
35957
|
});
|
|
35958
|
+
(_this$props$analytics2 = this.props.analytics) == null || _this$props$analytics2.onAnalyticsEvent({
|
|
35959
|
+
type: "perseus:label-image:marker-interacted-with:ti",
|
|
35960
|
+
payload: null
|
|
35961
|
+
});
|
|
36921
35962
|
const {
|
|
36922
35963
|
activeMarkerIndex
|
|
36923
35964
|
} = this.state;
|
|
@@ -37060,11 +36101,16 @@ class LabelImage extends React.Component {
|
|
|
37060
36101
|
})),
|
|
37061
36102
|
multipleSelect: this.props.multipleAnswers,
|
|
37062
36103
|
onChange: selection => {
|
|
37063
|
-
var _this$props$
|
|
37064
|
-
(
|
|
36104
|
+
var _this$props$analytics3, _this$props$analytics4;
|
|
36105
|
+
// TODO(LEMS-2829): Remove analytics event in LEMS-2829 in favor of ti below.
|
|
36106
|
+
(_this$props$analytics3 = this.props.analytics) == null || _this$props$analytics3.onAnalyticsEvent({
|
|
37065
36107
|
type: "perseus:label-image:choiced-interacted-with",
|
|
37066
36108
|
payload: null
|
|
37067
36109
|
});
|
|
36110
|
+
(_this$props$analytics4 = this.props.analytics) == null || _this$props$analytics4.onAnalyticsEvent({
|
|
36111
|
+
type: "perseus:label-image:choiced-interacted-with:ti",
|
|
36112
|
+
payload: null
|
|
36113
|
+
});
|
|
37068
36114
|
this.handleAnswerChoicesChangeForMarker(index, selection);
|
|
37069
36115
|
},
|
|
37070
36116
|
onToggle: opened => this.activateMarker(index, opened)
|
|
@@ -37160,11 +36206,16 @@ class LabelImage extends React.Component {
|
|
|
37160
36206
|
}))), this.renderMarkers()), /*#__PURE__*/React.createElement(HideAnswersToggle, {
|
|
37161
36207
|
areAnswersHidden: this.state.hideAnswers,
|
|
37162
36208
|
onChange: hideAnswers => {
|
|
37163
|
-
var _this$props$
|
|
37164
|
-
(
|
|
36209
|
+
var _this$props$analytics5, _this$props$analytics6;
|
|
36210
|
+
// TODO(LEMS-2831): Remove analytics event in LEMS-2831 in favor of ti below.
|
|
36211
|
+
(_this$props$analytics5 = this.props.analytics) == null || _this$props$analytics5.onAnalyticsEvent({
|
|
37165
36212
|
type: "perseus:label-image:toggle-answers-hidden",
|
|
37166
36213
|
payload: null
|
|
37167
36214
|
});
|
|
36215
|
+
(_this$props$analytics6 = this.props.analytics) == null || _this$props$analytics6.onAnalyticsEvent({
|
|
36216
|
+
type: "perseus:label-image:toggle-answers-hidden:ti",
|
|
36217
|
+
payload: null
|
|
36218
|
+
});
|
|
37168
36219
|
this.setState({
|
|
37169
36220
|
hideAnswers
|
|
37170
36221
|
});
|
|
@@ -37227,8 +36278,7 @@ var LabelImage$1 = {
|
|
|
37227
36278
|
displayName: "Label Image",
|
|
37228
36279
|
widget: LabelImageWithDependencies,
|
|
37229
36280
|
accessible: true,
|
|
37230
|
-
isLintable: true
|
|
37231
|
-
getPublicWidgetOptions: getLabelImagePublicWidgetOptions
|
|
36281
|
+
isLintable: true
|
|
37232
36282
|
};
|
|
37233
36283
|
|
|
37234
36284
|
var ItemState = /*#__PURE__*/function (ItemState) {
|
|
@@ -37483,20 +36533,24 @@ class Draggable extends React.Component {
|
|
|
37483
36533
|
if (this.props.margin != null) {
|
|
37484
36534
|
style.margin = this.props.margin;
|
|
37485
36535
|
}
|
|
37486
|
-
return
|
|
37487
|
-
|
|
37488
|
-
|
|
37489
|
-
|
|
37490
|
-
|
|
37491
|
-
|
|
37492
|
-
|
|
37493
|
-
|
|
37494
|
-
|
|
37495
|
-
|
|
37496
|
-
|
|
37497
|
-
|
|
37498
|
-
|
|
37499
|
-
|
|
36536
|
+
return (
|
|
36537
|
+
/*#__PURE__*/
|
|
36538
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
36539
|
+
React.createElement("li", {
|
|
36540
|
+
className: className,
|
|
36541
|
+
style: style,
|
|
36542
|
+
onMouseDown: this.onMouseDown,
|
|
36543
|
+
onTouchStart: this.onMouseDown,
|
|
36544
|
+
onTouchMove: this.onMouseMove,
|
|
36545
|
+
onTouchEnd: this.onMouseUp,
|
|
36546
|
+
onTouchCancel: this.onMouseUp
|
|
36547
|
+
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
36548
|
+
content: this.props.content,
|
|
36549
|
+
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "draggable"),
|
|
36550
|
+
onRender: this.props.onRender,
|
|
36551
|
+
strings: this.context.strings
|
|
36552
|
+
}))
|
|
36553
|
+
);
|
|
37500
36554
|
}
|
|
37501
36555
|
}
|
|
37502
36556
|
Draggable.contextType = PerseusI18nContext;
|
|
@@ -40139,8 +39193,7 @@ var NumberLine$1 = {
|
|
|
40139
39193
|
displayName: "Number line",
|
|
40140
39194
|
widget: NumberLine,
|
|
40141
39195
|
transform: numberLineTransform,
|
|
40142
|
-
staticTransform: staticTransform$1
|
|
40143
|
-
getPublicWidgetOptions: getNumberLinePublicWidgetOptions
|
|
39196
|
+
staticTransform: staticTransform$1
|
|
40144
39197
|
};
|
|
40145
39198
|
|
|
40146
39199
|
const getPromptJSON$5 = (renderProps, userInput) => {
|
|
@@ -40308,19 +39361,23 @@ class Card extends React.Component {
|
|
|
40308
39361
|
// Pull out the content to get rendered
|
|
40309
39362
|
const rendererProps = _.pick(this.props, "content");
|
|
40310
39363
|
const onMouseDown = this.props.animating ? $.noop : this.onMouseDown;
|
|
40311
|
-
return
|
|
40312
|
-
|
|
40313
|
-
|
|
40314
|
-
|
|
40315
|
-
|
|
40316
|
-
|
|
40317
|
-
|
|
40318
|
-
|
|
40319
|
-
|
|
40320
|
-
|
|
40321
|
-
|
|
40322
|
-
|
|
40323
|
-
|
|
39364
|
+
return (
|
|
39365
|
+
/*#__PURE__*/
|
|
39366
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions -- TODO(LEMS-2871): Address a11y error
|
|
39367
|
+
React.createElement("div", {
|
|
39368
|
+
className: "card-wrap",
|
|
39369
|
+
style: style,
|
|
39370
|
+
onMouseDown: onMouseDown,
|
|
39371
|
+
onTouchStart: onMouseDown,
|
|
39372
|
+
onTouchEnd: this.onMouseUp,
|
|
39373
|
+
onTouchCancel: this.onMouseUp
|
|
39374
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
39375
|
+
className: className.join(" ")
|
|
39376
|
+
}, /*#__PURE__*/React.createElement(Renderer, _extends({}, rendererProps, {
|
|
39377
|
+
linterContext: this.props.linterContext,
|
|
39378
|
+
strings: this.context.strings
|
|
39379
|
+
}))))
|
|
39380
|
+
);
|
|
40324
39381
|
}
|
|
40325
39382
|
}
|
|
40326
39383
|
Card.contextType = PerseusI18nContext;
|
|
@@ -40700,8 +39757,7 @@ var Orderer$1 = {
|
|
|
40700
39757
|
displayName: "Orderer",
|
|
40701
39758
|
hidden: true,
|
|
40702
39759
|
widget: Orderer,
|
|
40703
|
-
isLintable: true
|
|
40704
|
-
getPublicWidgetOptions: getOrdererPublicWidgetOptions
|
|
39760
|
+
isLintable: true
|
|
40705
39761
|
};
|
|
40706
39762
|
|
|
40707
39763
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -41923,8 +40979,7 @@ var Sorter$1 = {
|
|
|
41923
40979
|
name: "sorter",
|
|
41924
40980
|
displayName: "Sorter",
|
|
41925
40981
|
widget: Sorter,
|
|
41926
|
-
isLintable: true
|
|
41927
|
-
getPublicWidgetOptions: getSorterPublicWidgetOptions
|
|
40982
|
+
isLintable: true
|
|
41928
40983
|
};
|
|
41929
40984
|
|
|
41930
40985
|
const {
|
|
@@ -42322,7 +41377,7 @@ const init = function init() {
|
|
|
42322
41377
|
|
|
42323
41378
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
42324
41379
|
const libName = "@khanacademy/perseus";
|
|
42325
|
-
const libVersion = "
|
|
41380
|
+
const libVersion = "54.0.0";
|
|
42326
41381
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
42327
41382
|
|
|
42328
41383
|
const apiVersion = {
|
|
@@ -43809,6 +42864,18 @@ function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
|
|
|
43809
42864
|
const widget = widgetMap[WidgetId];
|
|
43810
42865
|
return (_widget$type = widget == null ? void 0 : widget.type) != null ? _widget$type : null;
|
|
43811
42866
|
}
|
|
42867
|
+
function getWidgetSubTypeByWidgetId(widgetId, widgetMap) {
|
|
42868
|
+
var _widget$type2, _graph$type;
|
|
42869
|
+
const widget = widgetMap[widgetId];
|
|
42870
|
+
const widgetType = (_widget$type2 = widget == null ? void 0 : widget.type) != null ? _widget$type2 : null;
|
|
42871
|
+
switch (widgetType) {
|
|
42872
|
+
case "interactive-graph":
|
|
42873
|
+
const graph = widget.options.graph;
|
|
42874
|
+
return (_graph$type = graph == null ? void 0 : graph.type) != null ? _graph$type : null;
|
|
42875
|
+
default:
|
|
42876
|
+
return null;
|
|
42877
|
+
}
|
|
42878
|
+
}
|
|
43812
42879
|
|
|
43813
42880
|
/**
|
|
43814
42881
|
* Does the content have a specific type of widget?
|
|
@@ -44601,5 +43668,5 @@ const ScoringUtil = {
|
|
|
44601
43668
|
* Types
|
|
44602
43669
|
*/
|
|
44603
43670
|
|
|
44604
|
-
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, makeSafeUrl, mathOnlyParser, mediaQueries, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
43671
|
+
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetSubTypeByWidgetId, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, makeSafeUrl, mathOnlyParser, mediaQueries, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
44605
43672
|
//# sourceMappingURL=index.js.map
|