@khanacademy/perseus 53.0.0 → 53.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.js +524 -193
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +42 -6
- package/dist/es/strings.js.map +1 -1
- package/dist/index.js +545 -1410
- package/dist/index.js.map +1 -1
- package/dist/server-item-renderer.d.ts +0 -3
- package/dist/strings.d.ts +36 -0
- package/dist/strings.js +60 -6
- package/dist/strings.js.map +1 -1
- package/dist/types.d.ts +2 -14
- package/dist/widgets/expression/expression.d.ts +0 -6
- package/dist/widgets/grapher/grapher.d.ts +2 -0
- package/dist/widgets/iframe/iframe.d.ts +2 -1
- package/dist/widgets/interactive-graph.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/polygon.d.ts +3 -2
- package/dist/widgets/interactive-graphs/graphs/screenreader-text.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/utils.d.ts +16 -1
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +0 -2
- package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +0 -2
- package/dist/widgets/matrix/matrix.d.ts +2 -1
- package/dist/widgets/plotter/plotter.d.ts +2 -0
- package/dist/widgets/radio/radio.d.ts +2 -1
- package/dist/widgets/table/table.d.ts +2 -0
- package/package.json +46 -46
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +0 -4
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var Banner = require('@khanacademy/wonder-blocks-banner');
|
|
|
33
33
|
var wonderBlocksDropdown = require('@khanacademy/wonder-blocks-dropdown');
|
|
34
34
|
var caretDown = require('@phosphor-icons/core/regular/caret-down.svg');
|
|
35
35
|
var caretUp = require('@phosphor-icons/core/regular/caret-up.svg');
|
|
36
|
-
|
|
36
|
+
require('lodash.debounce');
|
|
37
37
|
var mafs = require('mafs');
|
|
38
38
|
var wonderBlocksIcon = require('@khanacademy/wonder-blocks-icon');
|
|
39
39
|
var questionIcon = require('@phosphor-icons/core/regular/question.svg');
|
|
@@ -82,7 +82,6 @@ var Button__default = /*#__PURE__*/_interopDefaultLegacy(Button);
|
|
|
82
82
|
var Banner__default = /*#__PURE__*/_interopDefaultLegacy(Banner);
|
|
83
83
|
var caretDown__default = /*#__PURE__*/_interopDefaultLegacy(caretDown);
|
|
84
84
|
var caretUp__default = /*#__PURE__*/_interopDefaultLegacy(caretUp);
|
|
85
|
-
var debounce__default = /*#__PURE__*/_interopDefaultLegacy(debounce$1);
|
|
86
85
|
var questionIcon__default = /*#__PURE__*/_interopDefaultLegacy(questionIcon);
|
|
87
86
|
var Switch__default = /*#__PURE__*/_interopDefaultLegacy(Switch);
|
|
88
87
|
var Pill__default = /*#__PURE__*/_interopDefaultLegacy(Pill);
|
|
@@ -379,7 +378,7 @@ const mockStrings = {
|
|
|
379
378
|
x,
|
|
380
379
|
y
|
|
381
380
|
} = _ref27;
|
|
382
|
-
return `Point ${num} at ${x} comma ${y}
|
|
381
|
+
return `Point ${num} at ${x} comma ${y}.`;
|
|
383
382
|
},
|
|
384
383
|
srInteractiveElements: _ref28 => {
|
|
385
384
|
let {
|
|
@@ -670,7 +669,50 @@ const mockStrings = {
|
|
|
670
669
|
point3Y
|
|
671
670
|
} = _ref61;
|
|
672
671
|
return `Parabola with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and ${point3X} comma ${point3Y}.`;
|
|
673
|
-
}
|
|
672
|
+
},
|
|
673
|
+
srPolygonGraph: "A polygon.",
|
|
674
|
+
srPolygonGraphCoordinatePlane: "A polygon on a coordinate plane.",
|
|
675
|
+
srPolygonGraphPointsNum: _ref62 => {
|
|
676
|
+
let {
|
|
677
|
+
num
|
|
678
|
+
} = _ref62;
|
|
679
|
+
return `The polygon has ${num} points.`;
|
|
680
|
+
},
|
|
681
|
+
srPolygonGraphPointsOne: "The polygon has 1 point.",
|
|
682
|
+
srPolygonElementsNum: _ref63 => {
|
|
683
|
+
let {
|
|
684
|
+
num
|
|
685
|
+
} = _ref63;
|
|
686
|
+
return `A polygon with ${num} points.`;
|
|
687
|
+
},
|
|
688
|
+
srPolygonElementsOne: "A polygon with 1 point.",
|
|
689
|
+
srPolygonPointAngleApprox: _ref64 => {
|
|
690
|
+
let {
|
|
691
|
+
angle
|
|
692
|
+
} = _ref64;
|
|
693
|
+
return `Angle approximately equal to ${angle} degrees.`;
|
|
694
|
+
},
|
|
695
|
+
srPolygonPointAngle: _ref65 => {
|
|
696
|
+
let {
|
|
697
|
+
angle
|
|
698
|
+
} = _ref65;
|
|
699
|
+
return `Angle equal to ${angle} degrees.`;
|
|
700
|
+
},
|
|
701
|
+
srPolygonSideLength: _ref66 => {
|
|
702
|
+
let {
|
|
703
|
+
pointNum,
|
|
704
|
+
length
|
|
705
|
+
} = _ref66;
|
|
706
|
+
return `A line segment, length equal to ${length} units, connects to point ${pointNum}.`;
|
|
707
|
+
},
|
|
708
|
+
srPolygonSideLengthApprox: _ref67 => {
|
|
709
|
+
let {
|
|
710
|
+
pointNum,
|
|
711
|
+
length
|
|
712
|
+
} = _ref67;
|
|
713
|
+
return `A line segment, length approximately equal to ${length} units, connects to point ${pointNum}.`;
|
|
714
|
+
},
|
|
715
|
+
srUnlimitedPolygonEmpty: "An empty coordinate plane."
|
|
674
716
|
// The above strings are used for interactive graph SR descriptions.
|
|
675
717
|
};
|
|
676
718
|
|
|
@@ -10059,7 +10101,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10059
10101
|
|
|
10060
10102
|
/* eslint-disable @babel/no-invalid-this */
|
|
10061
10103
|
const {
|
|
10062
|
-
clockwise: clockwise$
|
|
10104
|
+
clockwise: clockwise$2,
|
|
10063
10105
|
reverseVector: reverseVector$1
|
|
10064
10106
|
} = kmath.geometry;
|
|
10065
10107
|
function scaledDistanceFromAngle(angle) {
|
|
@@ -11297,7 +11339,7 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11297
11339
|
// Temporary objects
|
|
11298
11340
|
___default["default"].invoke(polygon.temp, "remove");
|
|
11299
11341
|
polygon.temp = [];
|
|
11300
|
-
const isClockwise = clockwise$
|
|
11342
|
+
const isClockwise = clockwise$2(polygon.coords);
|
|
11301
11343
|
|
|
11302
11344
|
// Update angle labels
|
|
11303
11345
|
if (polygon.angleLabels.length || polygon.showRightAngleMarkers.length) {
|
|
@@ -14730,7 +14772,7 @@ function sum(array) {
|
|
|
14730
14772
|
return memo + arg;
|
|
14731
14773
|
}, 0);
|
|
14732
14774
|
}
|
|
14733
|
-
function clockwise$
|
|
14775
|
+
function clockwise$1(points) {
|
|
14734
14776
|
const segments = ___default["default"].zip(points, points.slice(1).concat(points.slice(0, 1)));
|
|
14735
14777
|
const areas = ___default["default"].map(segments, function (segment) {
|
|
14736
14778
|
const p1 = segment[0];
|
|
@@ -14796,7 +14838,7 @@ const draw$1 = {
|
|
|
14796
14838
|
const graphie = this.graphie;
|
|
14797
14839
|
const self = this;
|
|
14798
14840
|
const coords = ___default["default"].invoke(state.points, "coord");
|
|
14799
|
-
const isClockwise = clockwise$
|
|
14841
|
+
const isClockwise = clockwise$1(coords);
|
|
14800
14842
|
const n = coords.length;
|
|
14801
14843
|
|
|
14802
14844
|
// graphie.labelAngle and similar methods attempt to re-use the label
|
|
@@ -16542,9 +16584,28 @@ class Zoomable extends React__namespace.Component {
|
|
|
16542
16584
|
if (!this._isMounted) {
|
|
16543
16585
|
return;
|
|
16544
16586
|
}
|
|
16587
|
+
|
|
16588
|
+
// When a zoomable item is contained within a collapsed parent (such as an Explanation widget),
|
|
16589
|
+
// the parent node has zero width, which causes the scale to be miscalculated.
|
|
16590
|
+
// Therefore, we check to see if the node has a valid width, and if it doesn't,
|
|
16591
|
+
// then we traverse up the DOM tree and look for the closest "perseus-renderer"
|
|
16592
|
+
// (which should have a width).
|
|
16593
|
+
// To be cautious, we also account for the possibility that we can't find any parents with a valid width
|
|
16594
|
+
// (not that they don't exist, just that we can't find them).
|
|
16595
|
+
let parentNode = this._node;
|
|
16596
|
+
let currentNode = parentNode;
|
|
16597
|
+
while (currentNode && currentNode.offsetWidth === 0) {
|
|
16598
|
+
// When traversing up the DOM tree, we need to start at the "parentElement"
|
|
16599
|
+
// because "closest()" can match the current element,
|
|
16600
|
+
// and if it does so, then we are stuck in an infinite loop.
|
|
16601
|
+
currentNode = currentNode.parentElement?.closest(".perseus-renderer") ?? null;
|
|
16602
|
+
}
|
|
16603
|
+
if (currentNode) {
|
|
16604
|
+
parentNode = currentNode;
|
|
16605
|
+
}
|
|
16545
16606
|
const parentBounds = {
|
|
16546
|
-
width:
|
|
16547
|
-
height:
|
|
16607
|
+
width: parentNode.offsetWidth,
|
|
16608
|
+
height: parentNode.offsetHeight
|
|
16548
16609
|
};
|
|
16549
16610
|
const childBounds = this.props.computeChildBounds(this._node, parentBounds);
|
|
16550
16611
|
|
|
@@ -23377,8 +23438,8 @@ const styles$s = aphrodite.StyleSheet.create({
|
|
|
23377
23438
|
crossOutLine: {
|
|
23378
23439
|
// Center the icon within the container.
|
|
23379
23440
|
position: "absolute",
|
|
23380
|
-
top: `
|
|
23381
|
-
left: `
|
|
23441
|
+
top: `2px`,
|
|
23442
|
+
left: `2px`
|
|
23382
23443
|
}
|
|
23383
23444
|
});
|
|
23384
23445
|
|
|
@@ -23405,43 +23466,6 @@ function ChoiceInner(props) {
|
|
|
23405
23466
|
}
|
|
23406
23467
|
return /*#__PURE__*/React__namespace.createElement(InlineIcon, iconMinus);
|
|
23407
23468
|
}
|
|
23408
|
-
|
|
23409
|
-
// Handle dynamic styling of the multiple choice icon. Most
|
|
23410
|
-
// MC icon styles are constant, but we do allow the caller
|
|
23411
|
-
// to specify the selected color, and thus must control styles
|
|
23412
|
-
// related to the selected state dynamically.
|
|
23413
|
-
function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct) {
|
|
23414
|
-
let backgroundColor;
|
|
23415
|
-
let borderColor;
|
|
23416
|
-
let color;
|
|
23417
|
-
if (!showCorrectness && pressed) {
|
|
23418
|
-
borderColor = wonderBlocksTokens.color.blue;
|
|
23419
|
-
color = wonderBlocksTokens.color.blue;
|
|
23420
|
-
backgroundColor = "transparent";
|
|
23421
|
-
} else if (checked) {
|
|
23422
|
-
const bg = showCorrectness && correct ? wonderBlocksTokens.color.green : wonderBlocksTokens.color.blue;
|
|
23423
|
-
color = white;
|
|
23424
|
-
backgroundColor = bg;
|
|
23425
|
-
borderColor = bg;
|
|
23426
|
-
} else {
|
|
23427
|
-
borderColor = wonderBlocksTokens.color.offBlack64;
|
|
23428
|
-
color = wonderBlocksTokens.color.offBlack64;
|
|
23429
|
-
}
|
|
23430
|
-
|
|
23431
|
-
// define shape
|
|
23432
|
-
let borderRadius;
|
|
23433
|
-
if (multipleSelect) {
|
|
23434
|
-
borderRadius = 3;
|
|
23435
|
-
} else {
|
|
23436
|
-
borderRadius = CHOICE_ICON_SIZE;
|
|
23437
|
-
}
|
|
23438
|
-
return {
|
|
23439
|
-
backgroundColor,
|
|
23440
|
-
borderColor,
|
|
23441
|
-
color,
|
|
23442
|
-
borderRadius
|
|
23443
|
-
};
|
|
23444
|
-
}
|
|
23445
23469
|
const ChoiceIcon = function (props) {
|
|
23446
23470
|
const {
|
|
23447
23471
|
checked,
|
|
@@ -23453,9 +23477,41 @@ const ChoiceIcon = function (props) {
|
|
|
23453
23477
|
multipleSelect,
|
|
23454
23478
|
pos,
|
|
23455
23479
|
previouslyAnswered,
|
|
23480
|
+
// Used in "review mode"/"show rationale" to show that option was previously chosen
|
|
23456
23481
|
pressed
|
|
23457
23482
|
} = props;
|
|
23458
|
-
|
|
23483
|
+
|
|
23484
|
+
// Accounts for incorrect choice still displaying as learner tries again
|
|
23485
|
+
const choiceIsChecked = checked || showCorrectness && !correct && previouslyAnswered;
|
|
23486
|
+
|
|
23487
|
+
// Core styling
|
|
23488
|
+
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];
|
|
23489
|
+
|
|
23490
|
+
// Color styling
|
|
23491
|
+
// Handle dynamic styling of the multiple choice icon. Most
|
|
23492
|
+
// MC icon styles are constant, but we do allow the caller
|
|
23493
|
+
// to specify the selected color, and thus must control styles
|
|
23494
|
+
// related to the selected state dynamically.
|
|
23495
|
+
let crossOutColor;
|
|
23496
|
+
if (showCorrectness && correct && checked) {
|
|
23497
|
+
choiceStyling.push(styles$r.choiceCorrect);
|
|
23498
|
+
crossOutColor = wonderBlocksTokens.color.green;
|
|
23499
|
+
} else if (showCorrectness && !correct && (checked || previouslyAnswered)) {
|
|
23500
|
+
choiceStyling.push(styles$r.choiceIncorrect);
|
|
23501
|
+
crossOutColor = wonderBlocksTokens.color.red;
|
|
23502
|
+
} else if (checked) {
|
|
23503
|
+
// Show filled neutral blue color (showCorrectness is false)
|
|
23504
|
+
choiceStyling.push(styles$r.choiceNeutral);
|
|
23505
|
+
crossOutColor = wonderBlocksTokens.color.blue;
|
|
23506
|
+
} else if (pressed) {
|
|
23507
|
+
// Show outlined neutral blue color (showCorrectness is false)
|
|
23508
|
+
choiceStyling.push(styles$r.activeNeutral);
|
|
23509
|
+
crossOutColor = wonderBlocksTokens.color.blue;
|
|
23510
|
+
} else {
|
|
23511
|
+
// choice is not checked
|
|
23512
|
+
choiceStyling.push(styles$r.uncheckedColors);
|
|
23513
|
+
crossOutColor = wonderBlocksTokens.color.offBlack64;
|
|
23514
|
+
}
|
|
23459
23515
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
23460
23516
|
className: aphrodite.css(styles$t.iconWrapper)
|
|
23461
23517
|
}, /*#__PURE__*/React__namespace.createElement(FocusRing, {
|
|
@@ -23463,9 +23519,8 @@ const ChoiceIcon = function (props) {
|
|
|
23463
23519
|
visible: focused || hovered,
|
|
23464
23520
|
multipleSelect: multipleSelect
|
|
23465
23521
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
23466
|
-
style: dynamicStyles,
|
|
23467
23522
|
"data-testid": "choice-icon__library-choice-icon",
|
|
23468
|
-
className: aphrodite.css(
|
|
23523
|
+
className: aphrodite.css(...choiceStyling)
|
|
23469
23524
|
// used in BaseRadio editMode to check
|
|
23470
23525
|
// if we actually clicked on the radio icon
|
|
23471
23526
|
,
|
|
@@ -23477,7 +23532,7 @@ const ChoiceIcon = function (props) {
|
|
|
23477
23532
|
showCorrectness: showCorrectness,
|
|
23478
23533
|
correct: correct
|
|
23479
23534
|
})))), crossedOut && /*#__PURE__*/React__namespace.createElement(CrossOutLine, {
|
|
23480
|
-
color:
|
|
23535
|
+
color: crossOutColor
|
|
23481
23536
|
}));
|
|
23482
23537
|
};
|
|
23483
23538
|
const styles$r = aphrodite.StyleSheet.create({
|
|
@@ -23489,8 +23544,7 @@ const styles$r = aphrodite.StyleSheet.create({
|
|
|
23489
23544
|
alignItems: "center",
|
|
23490
23545
|
justifyContent: "center"
|
|
23491
23546
|
},
|
|
23492
|
-
|
|
23493
|
-
// Make the circle
|
|
23547
|
+
choiceBase: {
|
|
23494
23548
|
width: CHOICE_ICON_SIZE,
|
|
23495
23549
|
height: CHOICE_ICON_SIZE,
|
|
23496
23550
|
boxSizing: "border-box",
|
|
@@ -23502,30 +23556,48 @@ const styles$r = aphrodite.StyleSheet.create({
|
|
|
23502
23556
|
// "bold font family" so that characters which fall back to the default
|
|
23503
23557
|
// font get bolded too.
|
|
23504
23558
|
fontWeight: "bold",
|
|
23505
|
-
fontSize: 12,
|
|
23506
23559
|
// Center the icon wrapper.
|
|
23507
23560
|
display: "flex",
|
|
23508
23561
|
alignItems: "center",
|
|
23509
|
-
justifyContent: "center"
|
|
23510
|
-
|
|
23511
|
-
|
|
23512
|
-
|
|
23513
|
-
|
|
23514
|
-
|
|
23515
|
-
lineHeight: "1px"
|
|
23516
|
-
},
|
|
23517
|
-
circleCorrect: {
|
|
23562
|
+
justifyContent: "center"
|
|
23563
|
+
},
|
|
23564
|
+
choiceHasLetter: {
|
|
23565
|
+
fontSize: 12
|
|
23566
|
+
},
|
|
23567
|
+
choiceHasIcon: {
|
|
23518
23568
|
fontSize: CHOICE_ICON_SIZE
|
|
23519
23569
|
},
|
|
23520
|
-
|
|
23521
|
-
|
|
23522
|
-
|
|
23523
|
-
|
|
23570
|
+
choiceIsChecked: {
|
|
23571
|
+
color: wonderBlocksTokens.color.white
|
|
23572
|
+
},
|
|
23573
|
+
choiceIsUnchecked: {
|
|
23574
|
+
color: wonderBlocksTokens.color.offBlack64
|
|
23575
|
+
},
|
|
23576
|
+
choiceCorrect: {
|
|
23577
|
+
backgroundColor: wonderBlocksTokens.color.green,
|
|
23578
|
+
borderColor: wonderBlocksTokens.color.green
|
|
23524
23579
|
},
|
|
23525
|
-
|
|
23580
|
+
choiceIncorrect: {
|
|
23526
23581
|
backgroundColor: wonderBlocksTokens.color.red,
|
|
23527
|
-
borderColor: wonderBlocksTokens.color.red
|
|
23528
|
-
|
|
23582
|
+
borderColor: wonderBlocksTokens.color.red
|
|
23583
|
+
},
|
|
23584
|
+
choiceNeutral: {
|
|
23585
|
+
backgroundColor: wonderBlocksTokens.color.blue,
|
|
23586
|
+
borderColor: wonderBlocksTokens.color.blue
|
|
23587
|
+
},
|
|
23588
|
+
activeNeutral: {
|
|
23589
|
+
color: wonderBlocksTokens.color.blue,
|
|
23590
|
+
borderColor: wonderBlocksTokens.color.blue,
|
|
23591
|
+
backgroundColor: "transparent"
|
|
23592
|
+
},
|
|
23593
|
+
multiSelectShape: {
|
|
23594
|
+
borderRadius: 3
|
|
23595
|
+
},
|
|
23596
|
+
singleSelectShape: {
|
|
23597
|
+
borderRadius: CHOICE_ICON_SIZE
|
|
23598
|
+
},
|
|
23599
|
+
uncheckedColors: {
|
|
23600
|
+
borderColor: wonderBlocksTokens.color.offBlack64
|
|
23529
23601
|
}
|
|
23530
23602
|
});
|
|
23531
23603
|
|
|
@@ -24673,7 +24745,8 @@ var Radio = {
|
|
|
24673
24745
|
staticTransform: transform,
|
|
24674
24746
|
version: perseusCore.radioLogic.version,
|
|
24675
24747
|
propUpgrades: perseusCore.radioLogic.widgetOptionsUpgrades,
|
|
24676
|
-
isLintable: true
|
|
24748
|
+
isLintable: true,
|
|
24749
|
+
getPublicWidgetOptions: perseusCore.getRadioPublicWidgetOptions
|
|
24677
24750
|
};
|
|
24678
24751
|
|
|
24679
24752
|
/* eslint-disable import/no-named-as-default */
|
|
@@ -26883,7 +26956,8 @@ var Grapher$1 = {
|
|
|
26883
26956
|
hidden: true,
|
|
26884
26957
|
widget: Grapher,
|
|
26885
26958
|
transform: propTransform$2,
|
|
26886
|
-
staticTransform: staticTransform$4
|
|
26959
|
+
staticTransform: staticTransform$4,
|
|
26960
|
+
getPublicWidgetOptions: perseusCore.getGrapherPublicWidgetOptions
|
|
26887
26961
|
};
|
|
26888
26962
|
|
|
26889
26963
|
const getPromptJSON$f = rendererJSON => {
|
|
@@ -27190,7 +27264,8 @@ var Iframe$1 = {
|
|
|
27190
27264
|
displayName: "Iframe (deprecated)",
|
|
27191
27265
|
widget: Iframe,
|
|
27192
27266
|
// Let's not expose it to all content creators yet
|
|
27193
|
-
hidden: true
|
|
27267
|
+
hidden: true,
|
|
27268
|
+
getPublicWidgetOptions: perseusCore.getIFramePublicWidgetOptions
|
|
27194
27269
|
};
|
|
27195
27270
|
|
|
27196
27271
|
const getPromptJSON$d = renderProps => {
|
|
@@ -29922,10 +29997,10 @@ function Vector(props) {
|
|
|
29922
29997
|
}));
|
|
29923
29998
|
}
|
|
29924
29999
|
|
|
29925
|
-
function srFormatNumber(a, locale) {
|
|
30000
|
+
function srFormatNumber(a, locale, maximumFractionDigits) {
|
|
29926
30001
|
// adding zero here converts negative zero to positive zero.
|
|
29927
30002
|
return (0 + a).toLocaleString(locale, {
|
|
29928
|
-
maximumFractionDigits: 3,
|
|
30003
|
+
maximumFractionDigits: maximumFractionDigits ?? 3,
|
|
29929
30004
|
useGrouping: false // no thousands separators
|
|
29930
30005
|
});
|
|
29931
30006
|
}
|
|
@@ -30104,6 +30179,95 @@ a, b, c) {
|
|
|
30104
30179
|
return [x1, x2];
|
|
30105
30180
|
}
|
|
30106
30181
|
|
|
30182
|
+
/**
|
|
30183
|
+
* Given a list of points, return the angle made by the point at index i
|
|
30184
|
+
* with the points at indices i - 1 and i + 1.
|
|
30185
|
+
*/
|
|
30186
|
+
function getAngleFromPoints(points, i) {
|
|
30187
|
+
// Make sure the index is valid.
|
|
30188
|
+
if (i < 0 || i >= points.length || !Number.isInteger(i)) {
|
|
30189
|
+
return null;
|
|
30190
|
+
}
|
|
30191
|
+
|
|
30192
|
+
// If there are fewer than 3 points, there is no angle.
|
|
30193
|
+
if (points.length < 3) {
|
|
30194
|
+
return null;
|
|
30195
|
+
}
|
|
30196
|
+
|
|
30197
|
+
// Get the points at the current index and the two adjacent points.
|
|
30198
|
+
const point = points.at(i);
|
|
30199
|
+
const pt1 = points.at(i - 1);
|
|
30200
|
+
const pt2 = points[(i + 1) % points.length];
|
|
30201
|
+
if (!point || !pt1 || !pt2) {
|
|
30202
|
+
return null;
|
|
30203
|
+
}
|
|
30204
|
+
|
|
30205
|
+
// Get the lengths of adjacent sides, making a triangle
|
|
30206
|
+
// we can use to calculate the angle.
|
|
30207
|
+
const a = mafs.vec.dist(point, pt1);
|
|
30208
|
+
const b = mafs.vec.dist(point, pt2);
|
|
30209
|
+
const c = mafs.vec.dist(pt1, pt2);
|
|
30210
|
+
|
|
30211
|
+
// Law of cosines
|
|
30212
|
+
const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
|
|
30213
|
+
return angle;
|
|
30214
|
+
}
|
|
30215
|
+
function getSideLengthsFromPoints(points, i, isPolygonOpen) {
|
|
30216
|
+
if (i < 0 || i >= points.length || !Number.isInteger(i)) {
|
|
30217
|
+
return [];
|
|
30218
|
+
}
|
|
30219
|
+
if (points.length < 2) {
|
|
30220
|
+
return [];
|
|
30221
|
+
}
|
|
30222
|
+
const returnArray = [];
|
|
30223
|
+
const point = points[i];
|
|
30224
|
+
// If this point is the first point, then side 1 is the
|
|
30225
|
+
// last point in the list.
|
|
30226
|
+
const point1Index = i === 0 ? points.length - 1 : i - 1;
|
|
30227
|
+
const point1 = points[point1Index];
|
|
30228
|
+
// Make sure the previous point is not the same
|
|
30229
|
+
// as the current point.
|
|
30230
|
+
const side1 = i !== point1Index ? mafs.vec.dist(point, point1) : null;
|
|
30231
|
+
// The first side of the first point does not exist if
|
|
30232
|
+
// the polygon is open.
|
|
30233
|
+
if (side1 && !(isPolygonOpen && i === 0)) {
|
|
30234
|
+
returnArray.push({
|
|
30235
|
+
pointIndex: point1Index,
|
|
30236
|
+
sideLength: side1
|
|
30237
|
+
});
|
|
30238
|
+
}
|
|
30239
|
+
|
|
30240
|
+
// See if there is a side 2.
|
|
30241
|
+
const point2Index = (i + 1) % points.length;
|
|
30242
|
+
const point2 = points[point2Index];
|
|
30243
|
+
// Make sure that the next point is not the same as the
|
|
30244
|
+
// current point, and don't repeat the first point.
|
|
30245
|
+
const side2 = i !== point2Index && point2Index !== point1Index ? mafs.vec.dist(point, point2) : null;
|
|
30246
|
+
// The second side of the last point does not exist if
|
|
30247
|
+
// the polygon is open.
|
|
30248
|
+
if (side2 && !(isPolygonOpen && i === points.length - 1)) {
|
|
30249
|
+
returnArray.push({
|
|
30250
|
+
pointIndex: point2Index,
|
|
30251
|
+
sideLength: side2
|
|
30252
|
+
});
|
|
30253
|
+
}
|
|
30254
|
+
return returnArray;
|
|
30255
|
+
}
|
|
30256
|
+
|
|
30257
|
+
/**
|
|
30258
|
+
* Determine whether to determine the string with the
|
|
30259
|
+
* exact side length or the approximate side length.
|
|
30260
|
+
*/
|
|
30261
|
+
function getPolygonSideString(sideLength, pointIndex, strings, locale) {
|
|
30262
|
+
return Number.isInteger(sideLength) ? strings.srPolygonSideLength({
|
|
30263
|
+
pointNum: pointIndex + 1,
|
|
30264
|
+
length: `${sideLength}`
|
|
30265
|
+
}) : strings.srPolygonSideLengthApprox({
|
|
30266
|
+
pointNum: pointIndex + 1,
|
|
30267
|
+
length: srFormatNumber(sideLength, locale, 1)
|
|
30268
|
+
});
|
|
30269
|
+
}
|
|
30270
|
+
|
|
30107
30271
|
const {
|
|
30108
30272
|
calculateAngleInDegrees: calculateAngleInDegrees$2
|
|
30109
30273
|
} = kmath.angles;
|
|
@@ -30551,7 +30715,7 @@ const SvgDefs = () => /*#__PURE__*/React__default["default"].createElement("defs
|
|
|
30551
30715
|
})));
|
|
30552
30716
|
|
|
30553
30717
|
const {
|
|
30554
|
-
clockwise
|
|
30718
|
+
clockwise
|
|
30555
30719
|
} = kmath.geometry;
|
|
30556
30720
|
const {
|
|
30557
30721
|
getAngleFromVertex: getAngleFromVertex$1
|
|
@@ -30566,7 +30730,7 @@ const PolygonAngle = _ref => {
|
|
|
30566
30730
|
snapTo
|
|
30567
30731
|
} = _ref;
|
|
30568
30732
|
const [centerX, centerY] = centerPoint;
|
|
30569
|
-
const areClockwise = clockwise
|
|
30733
|
+
const areClockwise = clockwise([centerPoint, ...endPoints]);
|
|
30570
30734
|
const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
|
|
30571
30735
|
const radius = 0.3;
|
|
30572
30736
|
const a = mafs.vec.dist(centerPoint, endPoints[0]);
|
|
@@ -30639,7 +30803,7 @@ const Angle = _ref2 => {
|
|
|
30639
30803
|
range
|
|
30640
30804
|
} = _ref2;
|
|
30641
30805
|
// Check if the points are clockwise or not, depending on whether we allow reflex angles
|
|
30642
|
-
const areClockwise = clockwise
|
|
30806
|
+
const areClockwise = clockwise([...coords, vertex]);
|
|
30643
30807
|
const shouldReverseCoords = areClockwise && !allowReflexAngles;
|
|
30644
30808
|
|
|
30645
30809
|
// Reverse the coordinates accordingly to ensure the angle is calculated correctly
|
|
@@ -31586,9 +31750,6 @@ function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
|
|
|
31586
31750
|
};
|
|
31587
31751
|
}
|
|
31588
31752
|
|
|
31589
|
-
const {
|
|
31590
|
-
clockwise
|
|
31591
|
-
} = kmath.geometry;
|
|
31592
31753
|
function getGradableGraph(state, initialGraph) {
|
|
31593
31754
|
if (!state.hasBeenInteractedWith) {
|
|
31594
31755
|
return {
|
|
@@ -31665,17 +31826,9 @@ function getGradableGraph(state, initialGraph) {
|
|
|
31665
31826
|
};
|
|
31666
31827
|
}
|
|
31667
31828
|
if (state.type === "angle" && initialGraph.type === "angle") {
|
|
31668
|
-
// We're going to reverse the coords for scoring if the angle is clockwise and
|
|
31669
|
-
// we don't allow reflex angles. This is because the angle is largely defined
|
|
31670
|
-
// by the order of the points in the coords array, and we want to maintain
|
|
31671
|
-
// the same angle scoring with the legacy graph (which had a bug).
|
|
31672
|
-
// (LEMS-2190): When we remove the legacy graph, move this logic to the scoring function.
|
|
31673
|
-
const areClockwise = clockwise([state.coords[0], state.coords[2], state.coords[1]]);
|
|
31674
|
-
const shouldReverseCoords = areClockwise && !state.allowReflexAngles;
|
|
31675
|
-
const coords = shouldReverseCoords ? state.coords.slice().reverse() : state.coords;
|
|
31676
31829
|
return {
|
|
31677
31830
|
...initialGraph,
|
|
31678
|
-
coords,
|
|
31831
|
+
coords: state.coords,
|
|
31679
31832
|
allowReflexAngles: state.allowReflexAngles
|
|
31680
31833
|
};
|
|
31681
31834
|
}
|
|
@@ -32327,17 +32480,20 @@ function describePointGraph(state, i18n) {
|
|
|
32327
32480
|
});
|
|
32328
32481
|
});
|
|
32329
32482
|
return strings.srInteractiveElements({
|
|
32330
|
-
elements: pointDescriptions.join("
|
|
32483
|
+
elements: pointDescriptions.join(" ")
|
|
32331
32484
|
});
|
|
32332
32485
|
}
|
|
32333
32486
|
|
|
32334
|
-
|
|
32487
|
+
const {
|
|
32488
|
+
convertRadiansToDegrees
|
|
32489
|
+
} = kmath.angles;
|
|
32490
|
+
function renderPolygonGraph(state, dispatch, i18n, markings) {
|
|
32335
32491
|
return {
|
|
32336
32492
|
graph: /*#__PURE__*/React__namespace.createElement(PolygonGraph, {
|
|
32337
32493
|
graphState: state,
|
|
32338
32494
|
dispatch: dispatch
|
|
32339
32495
|
}),
|
|
32340
|
-
interactiveElementsDescription:
|
|
32496
|
+
interactiveElementsDescription: getPolygonGraphDescription(state, i18n, markings)
|
|
32341
32497
|
};
|
|
32342
32498
|
}
|
|
32343
32499
|
const PolygonGraph = props => {
|
|
@@ -32422,7 +32578,7 @@ const PolygonGraph = props => {
|
|
|
32422
32578
|
focusVisible,
|
|
32423
32579
|
setFocusVisible
|
|
32424
32580
|
};
|
|
32425
|
-
return numSides === "unlimited" ? UnlimitedPolygonGraph
|
|
32581
|
+
return numSides === "unlimited" ? /*#__PURE__*/React__namespace.createElement(UnlimitedPolygonGraph, statefulProps) : /*#__PURE__*/React__namespace.createElement(LimitedPolygonGraph, statefulProps);
|
|
32426
32582
|
};
|
|
32427
32583
|
const LimitedPolygonGraph = statefulProps => {
|
|
32428
32584
|
const {
|
|
@@ -32447,8 +32603,39 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32447
32603
|
const {
|
|
32448
32604
|
disableKeyboardInteraction
|
|
32449
32605
|
} = graphConfig;
|
|
32606
|
+
const {
|
|
32607
|
+
strings,
|
|
32608
|
+
locale
|
|
32609
|
+
} = usePerseusI18n();
|
|
32610
|
+
const id = React__namespace.useId();
|
|
32611
|
+
const pointsOffArray = Array(points.length).fill("off");
|
|
32612
|
+
// When moving an element, set its aria-live to "polite" and the others
|
|
32613
|
+
// to "off". Otherwise, other connected elements that move at the same
|
|
32614
|
+
// time might override the currently focused element's aria live.
|
|
32615
|
+
const [ariaLives, setAriaLives] = React__namespace.useState([
|
|
32616
|
+
// First one represents the aria-live value for the polygon itself.
|
|
32617
|
+
"off",
|
|
32618
|
+
// The rest represent the points.
|
|
32619
|
+
...pointsOffArray]);
|
|
32450
32620
|
const lines = getLines(points);
|
|
32451
|
-
|
|
32621
|
+
const polygonPointsNumId = id + "-points-num";
|
|
32622
|
+
const polygonPointsId = id + "-points";
|
|
32623
|
+
|
|
32624
|
+
// Aria label srings
|
|
32625
|
+
const {
|
|
32626
|
+
srPolygonGraph,
|
|
32627
|
+
srPolygonGraphPointsNum,
|
|
32628
|
+
srPolygonGraphPoints,
|
|
32629
|
+
srPolygonElementsNum
|
|
32630
|
+
} = describePolygonGraph(statefulProps.graphState, {
|
|
32631
|
+
strings,
|
|
32632
|
+
locale
|
|
32633
|
+
}, statefulProps.graphConfig.markings);
|
|
32634
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32635
|
+
// Outer graph minimal description
|
|
32636
|
+
"aria-label": srPolygonGraph,
|
|
32637
|
+
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
32638
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
32452
32639
|
points: [...points],
|
|
32453
32640
|
color: "var(--movable-line-stroke-color)",
|
|
32454
32641
|
svgPolygonProps: {
|
|
@@ -32474,13 +32661,17 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32474
32661
|
});
|
|
32475
32662
|
}), showSides && lines.map((_ref, i) => {
|
|
32476
32663
|
let [start, end] = _ref;
|
|
32664
|
+
// Use x and y to find the position of the label
|
|
32477
32665
|
const [x, y] = mafs.vec.midpoint(start, end);
|
|
32478
|
-
const length =
|
|
32666
|
+
const length = mafs.vec.dist(start, end);
|
|
32667
|
+
// Check if the length needs to indicate
|
|
32668
|
+
// that it's an approximation.
|
|
32669
|
+
const isApprox = !Number.isInteger(length);
|
|
32479
32670
|
return /*#__PURE__*/React__namespace.createElement(TextLabel, {
|
|
32480
32671
|
key: "side-" + i,
|
|
32481
32672
|
x: x,
|
|
32482
32673
|
y: y
|
|
32483
|
-
},
|
|
32674
|
+
}, isApprox ? `≈ ${length.toFixed(snapTo === "sides" ? 0 : 1)}` : length);
|
|
32484
32675
|
}), /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
32485
32676
|
points: [...points],
|
|
32486
32677
|
color: "transparent",
|
|
@@ -32500,27 +32691,80 @@ const LimitedPolygonGraph = statefulProps => {
|
|
|
32500
32691
|
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
32501
32692
|
},
|
|
32502
32693
|
// Required for lines to darken on focus
|
|
32503
|
-
onFocus: () =>
|
|
32694
|
+
onFocus: () => {
|
|
32695
|
+
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
32696
|
+
setAriaLives(() => ["polite", ...pointsOffArray]);
|
|
32697
|
+
},
|
|
32504
32698
|
// Required for line weighting to update on blur. Without this,
|
|
32505
32699
|
// the user has to hover over the shape for it to update
|
|
32506
32700
|
onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
32507
|
-
className: "movable-polygon"
|
|
32508
|
-
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
point: point,
|
|
32513
|
-
sequenceNumber: i + 1,
|
|
32514
|
-
onMove: destination => {
|
|
32515
|
-
const now = Date.now();
|
|
32516
|
-
const targetFPS = 40;
|
|
32517
|
-
const moveThresholdTime = 1000 / targetFPS;
|
|
32518
|
-
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
32519
|
-
dispatch(actions.polygon.movePoint(i, destination));
|
|
32520
|
-
lastMoveTimeRef.current = now;
|
|
32521
|
-
}
|
|
32701
|
+
className: "movable-polygon",
|
|
32702
|
+
// Accessibility-related fields
|
|
32703
|
+
role: "button",
|
|
32704
|
+
"aria-label": srPolygonGraphPoints ? `${srPolygonElementsNum} ${srPolygonGraphPoints}` : srPolygonElementsNum,
|
|
32705
|
+
"aria-live": ariaLives[0]
|
|
32522
32706
|
}
|
|
32523
|
-
}))
|
|
32707
|
+
}), points.map((point, i) => {
|
|
32708
|
+
const angleId = `${id}-angle-${i}`;
|
|
32709
|
+
const side1Id = `${id}-point-${i}-side-1`;
|
|
32710
|
+
const side2Id = `${id}-point-${i}-side-2`;
|
|
32711
|
+
|
|
32712
|
+
// Limited polygons always have an angle at each vertex.
|
|
32713
|
+
const angle = getAngleFromPoints(points, i);
|
|
32714
|
+
const angleDegree = angle ? convertRadiansToDegrees(angle) : null;
|
|
32715
|
+
|
|
32716
|
+
// Limited polygons always two sides attached to each vertex.
|
|
32717
|
+
const sidesArray = getSideLengthsFromPoints(points, i);
|
|
32718
|
+
const {
|
|
32719
|
+
pointIndex: point1Index,
|
|
32720
|
+
sideLength: side1Length
|
|
32721
|
+
} = sidesArray[0];
|
|
32722
|
+
const {
|
|
32723
|
+
pointIndex: point2Index,
|
|
32724
|
+
sideLength: side2Length
|
|
32725
|
+
} = sidesArray[1];
|
|
32726
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32727
|
+
key: "point-" + i
|
|
32728
|
+
}, /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
32729
|
+
ariaDescribedBy: `${angleId} ${side1Id} ${side2Id}`,
|
|
32730
|
+
ariaLive: ariaLives[i + 1],
|
|
32731
|
+
constrain: constrain,
|
|
32732
|
+
point: point,
|
|
32733
|
+
sequenceNumber: i + 1,
|
|
32734
|
+
onMove: destination => {
|
|
32735
|
+
const now = Date.now();
|
|
32736
|
+
const targetFPS = 40;
|
|
32737
|
+
const moveThresholdTime = 1000 / targetFPS;
|
|
32738
|
+
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
32739
|
+
dispatch(actions.polygon.movePoint(i, destination));
|
|
32740
|
+
lastMoveTimeRef.current = now;
|
|
32741
|
+
}
|
|
32742
|
+
},
|
|
32743
|
+
onFocus: () => {
|
|
32744
|
+
const newPointAriaLives = [...pointsOffArray];
|
|
32745
|
+
newPointAriaLives[i] = "polite";
|
|
32746
|
+
// Whole polygon is "off", and the current
|
|
32747
|
+
// point is "polite".
|
|
32748
|
+
setAriaLives(["off", ...newPointAriaLives]);
|
|
32749
|
+
}
|
|
32750
|
+
}), angleDegree && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32751
|
+
id: angleId
|
|
32752
|
+
}, Number.isInteger(angleDegree) ? strings.srPolygonPointAngle({
|
|
32753
|
+
angle: angleDegree
|
|
32754
|
+
}) : strings.srPolygonPointAngleApprox({
|
|
32755
|
+
angle: srFormatNumber(angleDegree, locale, 1)
|
|
32756
|
+
})), /*#__PURE__*/React__namespace.createElement("g", {
|
|
32757
|
+
id: side1Id
|
|
32758
|
+
}, getPolygonSideString(side1Length, point1Index, strings, locale)), /*#__PURE__*/React__namespace.createElement("g", {
|
|
32759
|
+
id: side2Id
|
|
32760
|
+
}, getPolygonSideString(side2Length, point2Index, strings, locale)));
|
|
32761
|
+
}), /*#__PURE__*/React__namespace.createElement("g", {
|
|
32762
|
+
id: polygonPointsNumId,
|
|
32763
|
+
style: a11y.srOnly
|
|
32764
|
+
}, srPolygonGraphPointsNum), srPolygonGraphPoints && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32765
|
+
id: polygonPointsId,
|
|
32766
|
+
style: a11y.srOnly
|
|
32767
|
+
}, srPolygonGraphPoints));
|
|
32524
32768
|
};
|
|
32525
32769
|
const UnlimitedPolygonGraph = statefulProps => {
|
|
32526
32770
|
const {
|
|
@@ -32535,6 +32779,19 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32535
32779
|
coords,
|
|
32536
32780
|
closedPolygon
|
|
32537
32781
|
} = statefulProps.graphState;
|
|
32782
|
+
const {
|
|
32783
|
+
strings,
|
|
32784
|
+
locale
|
|
32785
|
+
} = usePerseusI18n();
|
|
32786
|
+
const id = React__namespace.useId();
|
|
32787
|
+
const polygonPointsNumId = id + "-points-num";
|
|
32788
|
+
const polygonPointsId = id + "-points";
|
|
32789
|
+
|
|
32790
|
+
// When moving an element, set its aria-live to "polite" and the others
|
|
32791
|
+
// to "off". Otherwise, other connected elements that move at the same
|
|
32792
|
+
// time might override the currently focused element's aria live.
|
|
32793
|
+
const pointsOffArray = Array(points.length).fill("off");
|
|
32794
|
+
const [ariaLives, setAriaLives] = React__namespace.useState(pointsOffArray);
|
|
32538
32795
|
|
|
32539
32796
|
// If the polygon is closed, return a LimitedPolygon component.
|
|
32540
32797
|
if (closedPolygon) {
|
|
@@ -32549,7 +32806,22 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32549
32806
|
} = graphConfig;
|
|
32550
32807
|
const widthPx = graphDimensionsInPixels[0];
|
|
32551
32808
|
const heightPx = graphDimensionsInPixels[1];
|
|
32552
|
-
|
|
32809
|
+
|
|
32810
|
+
// Aria label srings
|
|
32811
|
+
const emptyGraph = coords.length === 0;
|
|
32812
|
+
const {
|
|
32813
|
+
srPolygonGraph,
|
|
32814
|
+
srPolygonGraphPointsNum,
|
|
32815
|
+
srPolygonGraphPoints
|
|
32816
|
+
} = describePolygonGraph(statefulProps.graphState, {
|
|
32817
|
+
strings,
|
|
32818
|
+
locale
|
|
32819
|
+
}, statefulProps.graphConfig.markings);
|
|
32820
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32821
|
+
// Outer graph minimal description
|
|
32822
|
+
"aria-label": emptyGraph ? strings.srUnlimitedPolygonEmpty : srPolygonGraph,
|
|
32823
|
+
"aria-describedby": `${polygonPointsNumId} ${polygonPointsId}`
|
|
32824
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
|
|
32553
32825
|
points: [...points],
|
|
32554
32826
|
color: "var(--movable-line-stroke-color)",
|
|
32555
32827
|
svgPolylineProps: {
|
|
@@ -32574,28 +32846,71 @@ const UnlimitedPolygonGraph = statefulProps => {
|
|
|
32574
32846
|
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
32575
32847
|
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
32576
32848
|
}
|
|
32577
|
-
}), coords.map((point, i) =>
|
|
32578
|
-
|
|
32579
|
-
|
|
32580
|
-
|
|
32581
|
-
|
|
32582
|
-
|
|
32583
|
-
|
|
32584
|
-
|
|
32585
|
-
|
|
32586
|
-
|
|
32587
|
-
|
|
32588
|
-
|
|
32589
|
-
// If the point being clicked is the first point and
|
|
32590
|
-
// there's enough non-duplicated points to form
|
|
32591
|
-
// a polygon (3 or more), close the shape before
|
|
32592
|
-
// setting focus.
|
|
32593
|
-
if (i === 0 && getArrayWithoutDuplicates(coords).length >= 3) {
|
|
32594
|
-
dispatch(actions.polygon.closePolygon());
|
|
32595
|
-
}
|
|
32596
|
-
dispatch(actions.polygon.clickPoint(i));
|
|
32849
|
+
}), coords.map((point, i) => {
|
|
32850
|
+
const angleId = `${id}-angle-${i}`;
|
|
32851
|
+
let sideIds = "";
|
|
32852
|
+
|
|
32853
|
+
// This point only has an angle if it's not the first
|
|
32854
|
+
// or last point in this open polygon.
|
|
32855
|
+
const hasAngle = i > 0 && i < coords.length - 1;
|
|
32856
|
+
const angle = hasAngle ? getAngleFromPoints(points, i) : null;
|
|
32857
|
+
const angleDegree = angle ? convertRadiansToDegrees(angle) : null;
|
|
32858
|
+
const sidesArray = getSideLengthsFromPoints(points, i, true);
|
|
32859
|
+
for (let sideIndex = 0; sideIndex < sidesArray.length; sideIndex++) {
|
|
32860
|
+
sideIds += `${id}-point-${i}-side-${sideIndex} `;
|
|
32597
32861
|
}
|
|
32598
|
-
|
|
32862
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32863
|
+
key: "point-" + i
|
|
32864
|
+
}, /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
32865
|
+
ariaDescribedBy: `${angleId} ${sideIds}`,
|
|
32866
|
+
ariaLive: ariaLives[i],
|
|
32867
|
+
point: point,
|
|
32868
|
+
sequenceNumber: i + 1,
|
|
32869
|
+
onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
|
|
32870
|
+
ref: ref => {
|
|
32871
|
+
pointsRef.current[i] = ref;
|
|
32872
|
+
},
|
|
32873
|
+
onFocus: () => {
|
|
32874
|
+
dispatch(actions.polygon.focusPoint(i));
|
|
32875
|
+
const newPointAriaLives = [...pointsOffArray];
|
|
32876
|
+
newPointAriaLives[i] = "polite";
|
|
32877
|
+
// Whole polygon is "off", and the current
|
|
32878
|
+
// point is "polite".
|
|
32879
|
+
setAriaLives([...newPointAriaLives]);
|
|
32880
|
+
},
|
|
32881
|
+
onClick: () => {
|
|
32882
|
+
// If the point being clicked is the first point and
|
|
32883
|
+
// there's enough non-duplicated points to form
|
|
32884
|
+
// a polygon (3 or more), close the shape before
|
|
32885
|
+
// setting focus.
|
|
32886
|
+
if (i === 0 && getArrayWithoutDuplicates(coords).length >= 3) {
|
|
32887
|
+
dispatch(actions.polygon.closePolygon());
|
|
32888
|
+
}
|
|
32889
|
+
dispatch(actions.polygon.clickPoint(i));
|
|
32890
|
+
}
|
|
32891
|
+
}), angleDegree && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32892
|
+
id: angleId
|
|
32893
|
+
}, Number.isInteger(angleDegree) ? strings.srPolygonPointAngle({
|
|
32894
|
+
angle: angleDegree
|
|
32895
|
+
}) : strings.srPolygonPointAngleApprox({
|
|
32896
|
+
angle: srFormatNumber(angleDegree, locale, 1)
|
|
32897
|
+
})), sidesArray.map((_ref2, j) => {
|
|
32898
|
+
let {
|
|
32899
|
+
pointIndex,
|
|
32900
|
+
sideLength
|
|
32901
|
+
} = _ref2;
|
|
32902
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32903
|
+
key: `${id}-point-${i}-side-${j}`,
|
|
32904
|
+
id: `${id}-point-${i}-side-${j}`
|
|
32905
|
+
}, getPolygonSideString(sideLength, pointIndex, strings, locale));
|
|
32906
|
+
}));
|
|
32907
|
+
}), coords.length > 0 && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32908
|
+
id: polygonPointsNumId,
|
|
32909
|
+
style: a11y.srOnly
|
|
32910
|
+
}, srPolygonGraphPointsNum), srPolygonGraphPoints && /*#__PURE__*/React__namespace.createElement("g", {
|
|
32911
|
+
id: polygonPointsId,
|
|
32912
|
+
style: a11y.srOnly
|
|
32913
|
+
}, srPolygonGraphPoints));
|
|
32599
32914
|
};
|
|
32600
32915
|
function getLines(points) {
|
|
32601
32916
|
return points.map((point, i) => {
|
|
@@ -32614,6 +32929,54 @@ const hasFocusVisible = element => {
|
|
|
32614
32929
|
return matches(":focus");
|
|
32615
32930
|
}
|
|
32616
32931
|
};
|
|
32932
|
+
function getPolygonGraphDescription(state, i18n, markings) {
|
|
32933
|
+
const strings = describePolygonGraph(state, i18n, markings);
|
|
32934
|
+
return strings.srPolygonInteractiveElements;
|
|
32935
|
+
}
|
|
32936
|
+
function describePolygonGraph(state, i18n, markings) {
|
|
32937
|
+
const {
|
|
32938
|
+
strings,
|
|
32939
|
+
locale
|
|
32940
|
+
} = i18n;
|
|
32941
|
+
const {
|
|
32942
|
+
coords
|
|
32943
|
+
} = state;
|
|
32944
|
+
const isCoordinatePlane = markings === "axes" || markings === "graph";
|
|
32945
|
+
const hasOnePoint = coords.length === 1;
|
|
32946
|
+
|
|
32947
|
+
// Figure out graph aria label based on markings.
|
|
32948
|
+
const srPolygonGraph = isCoordinatePlane ? strings.srPolygonGraphCoordinatePlane : strings.srPolygonGraph;
|
|
32949
|
+
const srPolygonGraphPointsNum = hasOnePoint ? strings.srPolygonGraphPointsOne : strings.srPolygonGraphPointsNum({
|
|
32950
|
+
num: coords.length
|
|
32951
|
+
});
|
|
32952
|
+
let srPolygonGraphPoints;
|
|
32953
|
+
// Figure out graph description based on markings.
|
|
32954
|
+
// If the graph is not on a coordinate plane, we should not include
|
|
32955
|
+
// the points' coordinates in the description.
|
|
32956
|
+
if (isCoordinatePlane) {
|
|
32957
|
+
const pointsString = coords.map((coord, i) => {
|
|
32958
|
+
return strings.srPointAtCoordinates({
|
|
32959
|
+
num: i + 1,
|
|
32960
|
+
x: srFormatNumber(coord[0], locale),
|
|
32961
|
+
y: srFormatNumber(coord[1], locale)
|
|
32962
|
+
});
|
|
32963
|
+
});
|
|
32964
|
+
srPolygonGraphPoints = pointsString.join(" ");
|
|
32965
|
+
}
|
|
32966
|
+
const srPolygonElementsNum = hasOnePoint ? strings.srPolygonElementsOne : strings.srPolygonElementsNum({
|
|
32967
|
+
num: coords.length
|
|
32968
|
+
});
|
|
32969
|
+
const srPolygonInteractiveElements = coords.length > 0 ? strings.srInteractiveElements({
|
|
32970
|
+
elements: [srPolygonElementsNum, srPolygonGraphPoints].join(" ")
|
|
32971
|
+
}) : null;
|
|
32972
|
+
return {
|
|
32973
|
+
srPolygonGraph,
|
|
32974
|
+
srPolygonGraphPointsNum,
|
|
32975
|
+
srPolygonGraphPoints,
|
|
32976
|
+
srPolygonElementsNum,
|
|
32977
|
+
srPolygonInteractiveElements
|
|
32978
|
+
};
|
|
32979
|
+
}
|
|
32617
32980
|
|
|
32618
32981
|
function renderQuadraticGraph(state, dispatch, i18n) {
|
|
32619
32982
|
return {
|
|
@@ -33320,7 +33683,8 @@ const MafsGraph = props => {
|
|
|
33320
33683
|
} = renderGraphElements({
|
|
33321
33684
|
state,
|
|
33322
33685
|
dispatch,
|
|
33323
|
-
i18n
|
|
33686
|
+
i18n,
|
|
33687
|
+
markings: props.markings
|
|
33324
33688
|
});
|
|
33325
33689
|
return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
|
|
33326
33690
|
value: {
|
|
@@ -33355,7 +33719,7 @@ const MafsGraph = props => {
|
|
|
33355
33719
|
handleKeyboardEvent(event, state, dispatch);
|
|
33356
33720
|
},
|
|
33357
33721
|
"aria-label": fullGraphAriaLabel,
|
|
33358
|
-
"aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId, isUnlimitedGraphState(state) &&
|
|
33722
|
+
"aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId, isUnlimitedGraphState(state) && unlimitedGraphKeyboardPromptId),
|
|
33359
33723
|
ref: graphRef,
|
|
33360
33724
|
tabIndex: 0,
|
|
33361
33725
|
onFocus: event => {
|
|
@@ -33381,7 +33745,19 @@ const MafsGraph = props => {
|
|
|
33381
33745
|
bottom: 0,
|
|
33382
33746
|
left: 0
|
|
33383
33747
|
}
|
|
33384
|
-
}, (props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null)), /*#__PURE__*/React__namespace.createElement(
|
|
33748
|
+
}, (props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null)), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View
|
|
33749
|
+
// If we have locked figures, we set aria-hidden
|
|
33750
|
+
// to false so that screen readers can read the
|
|
33751
|
+
// locked figures. If we don't have any locked
|
|
33752
|
+
// figures, we need to set aria-hidden to true so
|
|
33753
|
+
// that screen readers don't read this Mafs element
|
|
33754
|
+
// as an empty image.
|
|
33755
|
+
// Note: Adding this in a wrapping View element
|
|
33756
|
+
// so that `aria-hidden` is a valid property.
|
|
33757
|
+
// It does not work on `Mafs` or svg elements.
|
|
33758
|
+
, {
|
|
33759
|
+
"aria-hidden": props.lockedFigures && props.lockedFigures.length > 0 ? false : true
|
|
33760
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
|
|
33385
33761
|
preserveAspectRatio: false,
|
|
33386
33762
|
viewBox: {
|
|
33387
33763
|
x: state.range[X],
|
|
@@ -33401,10 +33777,10 @@ const MafsGraph = props => {
|
|
|
33401
33777
|
height: height
|
|
33402
33778
|
})),
|
|
33403
33779
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
33404
|
-
(props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes,
|
|
33780
|
+
(props.markings === "graph" || props.markings === "axes") && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), props.lockedFigures && props.lockedFigures.length > 0 && /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
|
|
33405
33781
|
lockedFigures: props.lockedFigures,
|
|
33406
33782
|
range: state.range
|
|
33407
|
-
}))), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
|
|
33783
|
+
})))), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
|
|
33408
33784
|
lockedFigures: props.lockedFigures
|
|
33409
33785
|
}), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
33410
33786
|
style: {
|
|
@@ -33698,7 +34074,8 @@ const renderGraphElements = props => {
|
|
|
33698
34074
|
const {
|
|
33699
34075
|
state,
|
|
33700
34076
|
dispatch,
|
|
33701
|
-
i18n
|
|
34077
|
+
i18n,
|
|
34078
|
+
markings
|
|
33702
34079
|
} = props;
|
|
33703
34080
|
const {
|
|
33704
34081
|
type
|
|
@@ -33715,7 +34092,7 @@ const renderGraphElements = props => {
|
|
|
33715
34092
|
case "ray":
|
|
33716
34093
|
return renderRayGraph(state, dispatch);
|
|
33717
34094
|
case "polygon":
|
|
33718
|
-
return renderPolygonGraph(state, dispatch);
|
|
34095
|
+
return renderPolygonGraph(state, dispatch, i18n, markings);
|
|
33719
34096
|
case "point":
|
|
33720
34097
|
return renderPointGraph(state, dispatch);
|
|
33721
34098
|
case "circle":
|
|
@@ -34700,7 +35077,7 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
34700
35077
|
return [dx, dy];
|
|
34701
35078
|
};
|
|
34702
35079
|
// Less than or approximately equal
|
|
34703
|
-
function leq
|
|
35080
|
+
function leq(a, b) {
|
|
34704
35081
|
return a < b || perseusCore.approximateEqual(a, b);
|
|
34705
35082
|
}
|
|
34706
35083
|
function boundAndSnapToGrid(point, _ref) {
|
|
@@ -34871,7 +35248,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
34871
35248
|
|
|
34872
35249
|
// Avoid degenerate triangles
|
|
34873
35250
|
if (innerAngles.some(function (angle) {
|
|
34874
|
-
return leq
|
|
35251
|
+
return leq(angle, 1);
|
|
34875
35252
|
})) {
|
|
34876
35253
|
return startingPoint;
|
|
34877
35254
|
}
|
|
@@ -34933,7 +35310,7 @@ function boundAndSnapToSides(destinationPoint, _ref7, index) {
|
|
|
34933
35310
|
});
|
|
34934
35311
|
|
|
34935
35312
|
// Avoid degenerate triangles
|
|
34936
|
-
if (leq
|
|
35313
|
+
if (leq(sides[1] + sides[2], sides[0]) || leq(sides[0] + sides[2], sides[1]) || leq(sides[0] + sides[1], sides[2])) {
|
|
34937
35314
|
return startingPoint;
|
|
34938
35315
|
}
|
|
34939
35316
|
|
|
@@ -35100,24 +35477,10 @@ const {
|
|
|
35100
35477
|
sign,
|
|
35101
35478
|
vector
|
|
35102
35479
|
} = kmath.geometry;
|
|
35103
|
-
const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
|
|
35104
35480
|
const defaultBackgroundImage = {
|
|
35105
35481
|
url: null
|
|
35106
35482
|
};
|
|
35107
35483
|
const UNLIMITED = "unlimited";
|
|
35108
|
-
|
|
35109
|
-
// Sample background image:
|
|
35110
|
-
// https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
|
|
35111
|
-
|
|
35112
|
-
// default to defaultValue if actual is null or undefined
|
|
35113
|
-
function defaultVal(actual, defaultValue) {
|
|
35114
|
-
return actual == null ? defaultValue : actual;
|
|
35115
|
-
}
|
|
35116
|
-
|
|
35117
|
-
// Less than or approximately equal
|
|
35118
|
-
function leq(a, b) {
|
|
35119
|
-
return a < b || perseusCore.approximateEqual(a, b);
|
|
35120
|
-
}
|
|
35121
35484
|
function capitalize(str) {
|
|
35122
35485
|
return str.replace(/(?:^|-)(.)/g, function (match, letter) {
|
|
35123
35486
|
return letter.toUpperCase();
|
|
@@ -35126,14 +35489,6 @@ function capitalize(str) {
|
|
|
35126
35489
|
function numSteps(range, step) {
|
|
35127
35490
|
return Math.floor((range[1] - range[0]) / step);
|
|
35128
35491
|
}
|
|
35129
|
-
const _getShouldShowInstructions = props => {
|
|
35130
|
-
return _isClickToAddPoints(props) && (
|
|
35131
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
35132
|
-
props.graph.coords == null || props.graph.coords.length === 0);
|
|
35133
|
-
};
|
|
35134
|
-
const _isClickToAddPoints = props => {
|
|
35135
|
-
return props.graph.type === "point" && props.graph.numPoints === UNLIMITED || props.graph.type === "polygon" && props.graph.numSides === UNLIMITED;
|
|
35136
|
-
};
|
|
35137
35492
|
const makeInvalidTypeError = (functionName, graphType) => {
|
|
35138
35493
|
return new perseusCore.PerseusError(`${functionName} called but current graph type is not a '${graphType}'`, perseusCore.Errors.NotAllowed, {
|
|
35139
35494
|
metadata: {
|
|
@@ -35174,1200 +35529,6 @@ function getQuadraticCoefficients(coords) {
|
|
|
35174
35529
|
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;
|
|
35175
35530
|
return [a, b, c];
|
|
35176
35531
|
}
|
|
35177
|
-
|
|
35178
|
-
// (LEMS-2190): Move the Mafs Angle Graph coordinate reversal logic in interactive-graph-state.ts
|
|
35179
|
-
// to this file when we remove the legacy graph. This logic allows us to support bi-directional angles
|
|
35180
|
-
// for the new (non-reflexive) Mafs graphs, while maintaining the same scoring behaviour as the legacy graph.
|
|
35181
|
-
// Once the legacy graph is removed, we should move this logic directly into the validate function below.
|
|
35182
|
-
class LegacyInteractiveGraph extends React__namespace.Component {
|
|
35183
|
-
static contextType = PerseusI18nContext;
|
|
35184
|
-
angle;
|
|
35185
|
-
circle;
|
|
35186
|
-
graphie;
|
|
35187
|
-
horizHairline;
|
|
35188
|
-
line;
|
|
35189
|
-
lines;
|
|
35190
|
-
parabola;
|
|
35191
|
-
pointA;
|
|
35192
|
-
pointB;
|
|
35193
|
-
pointC;
|
|
35194
|
-
// @ts-expect-error - TS2564 - Property 'points' has no initializer and is not definitely assigned in the constructor.
|
|
35195
|
-
points;
|
|
35196
|
-
polygon;
|
|
35197
|
-
// @ts-expect-error - TS2564 - Property 'shouldResetGraphie' has no initializer and is not definitely assigned in the constructor.
|
|
35198
|
-
shouldResetGraphie;
|
|
35199
|
-
sinusoid;
|
|
35200
|
-
trashCan;
|
|
35201
|
-
vertHairline;
|
|
35202
|
-
static defaultProps = {
|
|
35203
|
-
labels: ["x", "y"],
|
|
35204
|
-
range: [[-10, 10], [-10, 10]],
|
|
35205
|
-
step: [1, 1],
|
|
35206
|
-
backgroundImage: defaultBackgroundImage,
|
|
35207
|
-
markings: "graph",
|
|
35208
|
-
showTooltips: false,
|
|
35209
|
-
showProtractor: false,
|
|
35210
|
-
graph: {
|
|
35211
|
-
type: "linear"
|
|
35212
|
-
}
|
|
35213
|
-
};
|
|
35214
|
-
state = {
|
|
35215
|
-
shouldShowInstructions: _getShouldShowInstructions(this.props)
|
|
35216
|
-
};
|
|
35217
|
-
componentDidMount() {
|
|
35218
|
-
if (this.refs.graph) {
|
|
35219
|
-
// eslint-disable-next-line react/no-string-refs
|
|
35220
|
-
// @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
|
|
35221
|
-
this.setGraphie(this.refs.graph.graphie());
|
|
35222
|
-
}
|
|
35223
|
-
}
|
|
35224
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
35225
|
-
if (this.isClickToAddPoints() !== this.isClickToAddPoints(nextProps)) {
|
|
35226
|
-
this.shouldResetGraphie = true;
|
|
35227
|
-
this.setState({
|
|
35228
|
-
shouldShowInstructions: _getShouldShowInstructions(nextProps)
|
|
35229
|
-
});
|
|
35230
|
-
}
|
|
35231
|
-
if (this.props.backgroundImage?.url !== nextProps.backgroundImage?.url || this.props.backgroundImage !== nextProps.backgroundImage || this.props.containerSizeClass !== nextProps.containerSizeClass) {
|
|
35232
|
-
this.shouldResetGraphie = true;
|
|
35233
|
-
}
|
|
35234
|
-
}
|
|
35235
|
-
componentDidUpdate(prevProps, prevState) {
|
|
35236
|
-
// TODO(jeremy): This feels underspecified. For example, if the props
|
|
35237
|
-
// change the coords of a segment graph, but not the numSegments, we'd
|
|
35238
|
-
// not count that as an update.
|
|
35239
|
-
const oldType = prevProps.graph.type;
|
|
35240
|
-
const newType = this.props.graph.type;
|
|
35241
|
-
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)) {
|
|
35242
|
-
this["remove" + capitalize(oldType) + "Controls"]();
|
|
35243
|
-
this["add" + capitalize(newType) + "Controls"]();
|
|
35244
|
-
}
|
|
35245
|
-
if (this.shouldResetGraphie) {
|
|
35246
|
-
this.resetGraphie();
|
|
35247
|
-
}
|
|
35248
|
-
}
|
|
35249
|
-
_getShouldShowInstructions = props => {
|
|
35250
|
-
props = props || this.props;
|
|
35251
|
-
return this.isClickToAddPoints(props) && (
|
|
35252
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
35253
|
-
props.graph.coords == null || props.graph.coords.length === 0);
|
|
35254
|
-
};
|
|
35255
|
-
setGraphie = newGraphie => {
|
|
35256
|
-
this.graphie = newGraphie;
|
|
35257
|
-
this.setupGraphie();
|
|
35258
|
-
};
|
|
35259
|
-
handleAddPointsMouseDown = coord => {
|
|
35260
|
-
// This function should only be called when this.isClickToAddPoints()
|
|
35261
|
-
// is true
|
|
35262
|
-
if (!this.isClickToAddPoints()) {
|
|
35263
|
-
throw new perseusCore.PerseusError("handleAddPointsClick should not be registered" + "when isClickToAddPoints() is false", perseusCore.Errors.NotAllowed);
|
|
35264
|
-
}
|
|
35265
|
-
if (!this.isCoordInTrash(coord)) {
|
|
35266
|
-
let point;
|
|
35267
|
-
if (this.props.graph.type === "point") {
|
|
35268
|
-
point = this.createPointForPointsType(coord, this.points.length);
|
|
35269
|
-
if (!point.constrain()) {
|
|
35270
|
-
point.remove();
|
|
35271
|
-
return;
|
|
35272
|
-
}
|
|
35273
|
-
this.points.push(point);
|
|
35274
|
-
|
|
35275
|
-
// interactive2 allows us to grab the point
|
|
35276
|
-
const idx = this.points.length - 1;
|
|
35277
|
-
this.points[idx].grab(coord);
|
|
35278
|
-
this.updateCoordsFromPoints();
|
|
35279
|
-
} else if (this.props.graph.type === "polygon") {
|
|
35280
|
-
if (this.polygon?.closed()) {
|
|
35281
|
-
return;
|
|
35282
|
-
}
|
|
35283
|
-
point = this.createPointForPolygonType(coord, this.points.length);
|
|
35284
|
-
this.points.push(point);
|
|
35285
|
-
const idx = this.points.length - 1;
|
|
35286
|
-
this.points[idx].grab(coord);
|
|
35287
|
-
|
|
35288
|
-
// We don't call updateCoordsFromPoints for
|
|
35289
|
-
// polygons, since the polygon won't be
|
|
35290
|
-
// closed yet.
|
|
35291
|
-
this.updatePolygon();
|
|
35292
|
-
}
|
|
35293
|
-
this.setState({
|
|
35294
|
-
shouldShowInstructions: false
|
|
35295
|
-
});
|
|
35296
|
-
}
|
|
35297
|
-
};
|
|
35298
|
-
resetGraphie = () => {
|
|
35299
|
-
this.shouldResetGraphie = false;
|
|
35300
|
-
this.parabola = null;
|
|
35301
|
-
this.sinusoid = null;
|
|
35302
|
-
// eslint-disable-next-line react/no-string-refs
|
|
35303
|
-
// @ts-expect-error - TS2339 - Property 'reset' does not exist on type 'ReactInstance'.
|
|
35304
|
-
this.refs.graph?.reset();
|
|
35305
|
-
};
|
|
35306
|
-
setupGraphie = () => {
|
|
35307
|
-
this.setTrashCanVisibility(0);
|
|
35308
|
-
if (this.isClickToAddPoints()) {
|
|
35309
|
-
this.setTrashCanVisibility(0.5);
|
|
35310
|
-
}
|
|
35311
|
-
if (this.props.apiOptions.isMobile) {
|
|
35312
|
-
this.horizHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
|
|
35313
|
-
normalStyle: {
|
|
35314
|
-
strokeWidth: 1
|
|
35315
|
-
}
|
|
35316
|
-
});
|
|
35317
|
-
this.horizHairline.attr({
|
|
35318
|
-
stroke: KhanColors.INTERACTIVE
|
|
35319
|
-
});
|
|
35320
|
-
this.horizHairline?.hide();
|
|
35321
|
-
this.vertHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
|
|
35322
|
-
normalStyle: {
|
|
35323
|
-
strokeWidth: 1
|
|
35324
|
-
}
|
|
35325
|
-
});
|
|
35326
|
-
this.vertHairline.attr({
|
|
35327
|
-
stroke: KhanColors.INTERACTIVE
|
|
35328
|
-
});
|
|
35329
|
-
this.vertHairline?.hide();
|
|
35330
|
-
}
|
|
35331
|
-
const type = this.props.graph.type;
|
|
35332
|
-
this["add" + capitalize(type) + "Controls"]();
|
|
35333
|
-
};
|
|
35334
|
-
showHairlines = point => {
|
|
35335
|
-
if (this.props.apiOptions.isMobile && this.props.markings !== "none") {
|
|
35336
|
-
// Hairlines are already initialized when the graph is loaded, so
|
|
35337
|
-
// here we just move them to the updated location and make them
|
|
35338
|
-
// visible.
|
|
35339
|
-
this.horizHairline?.moveTo([this.props.range[0][0], point[1]], [this.props.range[0][1], point[1]]);
|
|
35340
|
-
this.horizHairline?.show();
|
|
35341
|
-
this.vertHairline?.moveTo([point[0], this.props.range[1][0]], [point[0], this.props.range[1][1]]);
|
|
35342
|
-
this.vertHairline?.show();
|
|
35343
|
-
}
|
|
35344
|
-
};
|
|
35345
|
-
hideHairlines = () => {
|
|
35346
|
-
if (this.props.apiOptions.isMobile) {
|
|
35347
|
-
this.horizHairline?.hide();
|
|
35348
|
-
this.vertHairline?.hide();
|
|
35349
|
-
}
|
|
35350
|
-
};
|
|
35351
|
-
setTrashCanVisibility = opacity => {
|
|
35352
|
-
const graphie = this.graphie;
|
|
35353
|
-
if (kmath.number.equal(opacity, 0)) {
|
|
35354
|
-
if (this.trashCan) {
|
|
35355
|
-
this.trashCan.remove();
|
|
35356
|
-
this.trashCan = null;
|
|
35357
|
-
}
|
|
35358
|
-
} else if (!this.props.apiOptions.isMobile) {
|
|
35359
|
-
// Only if trash tooltips are not being used, we initialize the old
|
|
35360
|
-
// trash can area.
|
|
35361
|
-
if (!this.trashCan) {
|
|
35362
|
-
this.trashCan = graphie?.raphael?.image(TRASH_ICON_URI, graphie.xpixels - 40, graphie.ypixels - 40, 40, 40);
|
|
35363
|
-
}
|
|
35364
|
-
this.trashCan?.attr({
|
|
35365
|
-
opacity: opacity
|
|
35366
|
-
});
|
|
35367
|
-
}
|
|
35368
|
-
};
|
|
35369
|
-
isClickToAddPoints = props => {
|
|
35370
|
-
props = props || this.props;
|
|
35371
|
-
return _isClickToAddPoints(props);
|
|
35372
|
-
};
|
|
35373
|
-
areAngleGraphsEqual(prevGraph, currentGraph) {
|
|
35374
|
-
return prevGraph.allowReflexAngles !== currentGraph.allowReflexAngles || prevGraph.angleOffsetDeg !== currentGraph.angleOffsetDeg || prevGraph.snapDegrees !== currentGraph.snapDegrees;
|
|
35375
|
-
}
|
|
35376
|
-
arePointGraphsEqual(prevGraph, currentGraph) {
|
|
35377
|
-
return prevGraph.numPoints !== currentGraph.numPoints;
|
|
35378
|
-
}
|
|
35379
|
-
arePolygonGraphsEqual(prevGraph, currentGraph) {
|
|
35380
|
-
return prevGraph.numSides !== currentGraph.numSides || prevGraph.showAngles !== currentGraph.showAngles || prevGraph.showSides !== currentGraph.showSides || prevGraph.snapTo !== currentGraph.snapTo;
|
|
35381
|
-
}
|
|
35382
|
-
areSegmentGraphsEqual(prevGraph, currentGraph) {
|
|
35383
|
-
return prevGraph.numSegments !== currentGraph.numSegments;
|
|
35384
|
-
}
|
|
35385
|
-
_lineStroke = () => {
|
|
35386
|
-
// This should probably use: this.props.apiOptions.isMobile
|
|
35387
|
-
// @ts-expect-error - TS2339 - Property 'isMobile' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
|
|
35388
|
-
return this.props.isMobile ? {
|
|
35389
|
-
"stroke-width": 3
|
|
35390
|
-
} : {};
|
|
35391
|
-
};
|
|
35392
|
-
addLine = type => {
|
|
35393
|
-
const self = this;
|
|
35394
|
-
const graphie = self.graphie;
|
|
35395
|
-
const coords = InteractiveGraph.getLineCoords(self.props.graph, self.props);
|
|
35396
|
-
const points = self.points = ___default["default"].map(coords, coord => {
|
|
35397
|
-
return Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35398
|
-
coord: coord,
|
|
35399
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap()],
|
|
35400
|
-
onMove: () => {
|
|
35401
|
-
const graph = ___default["default"].extend({}, self.props.graph, {
|
|
35402
|
-
coords: ___default["default"].invoke(points, "coord")
|
|
35403
|
-
});
|
|
35404
|
-
self.onChange({
|
|
35405
|
-
graph: graph
|
|
35406
|
-
});
|
|
35407
|
-
}
|
|
35408
|
-
});
|
|
35409
|
-
});
|
|
35410
|
-
const lineConfig = {
|
|
35411
|
-
points: points,
|
|
35412
|
-
static: true,
|
|
35413
|
-
normalStyle: {
|
|
35414
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE,
|
|
35415
|
-
...this._lineStroke()
|
|
35416
|
-
}
|
|
35417
|
-
};
|
|
35418
|
-
if (type === "line") {
|
|
35419
|
-
// @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"; }; }'.
|
|
35420
|
-
lineConfig.extendLine = true;
|
|
35421
|
-
} else if (type === "ray") {
|
|
35422
|
-
// @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"; }; }'.
|
|
35423
|
-
lineConfig.extendRay = true;
|
|
35424
|
-
}
|
|
35425
|
-
self.line = Interactive2.addMovableLine(graphie, lineConfig);
|
|
35426
|
-
|
|
35427
|
-
// A and B can't be in the same place
|
|
35428
|
-
points[0].listen("constraints", "isLine", coord => {
|
|
35429
|
-
return !kmath.point.equal(coord, points[1].coord());
|
|
35430
|
-
});
|
|
35431
|
-
points[1].listen("constraints", "isLine", coord => {
|
|
35432
|
-
return !kmath.point.equal(coord, points[0].coord());
|
|
35433
|
-
});
|
|
35434
|
-
};
|
|
35435
|
-
removeLine = () => {
|
|
35436
|
-
___default["default"].invoke(this.points, "remove");
|
|
35437
|
-
this.line?.remove();
|
|
35438
|
-
};
|
|
35439
|
-
addNoneControls = () => {};
|
|
35440
|
-
removeNoneControls = () => {};
|
|
35441
|
-
addLinearControls = () => {
|
|
35442
|
-
this.addLine("line");
|
|
35443
|
-
};
|
|
35444
|
-
removeLinearControls = () => {
|
|
35445
|
-
this.removeLine();
|
|
35446
|
-
};
|
|
35447
|
-
addQuadraticControls = () => {
|
|
35448
|
-
if (this.props.graph.type !== "quadratic") {
|
|
35449
|
-
throw makeInvalidTypeError("addQuadraticControls", "quadratic");
|
|
35450
|
-
}
|
|
35451
|
-
let coords = this.props.graph.coords;
|
|
35452
|
-
if (!coords) {
|
|
35453
|
-
coords = InteractiveGraph.defaultQuadraticCoords(this.props);
|
|
35454
|
-
}
|
|
35455
|
-
const onMoveHandler = () => {
|
|
35456
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
35457
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'.
|
|
35458
|
-
coords: [pointA.coord(), pointB.coord(), pointC.coord()]
|
|
35459
|
-
});
|
|
35460
|
-
this.onChange({
|
|
35461
|
-
graph
|
|
35462
|
-
});
|
|
35463
|
-
this.updateQuadratic();
|
|
35464
|
-
};
|
|
35465
|
-
|
|
35466
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35467
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35468
|
-
// to be executed synchronously, which causes this issue.
|
|
35469
|
-
let pointA = null;
|
|
35470
|
-
pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35471
|
-
coord: coords[0],
|
|
35472
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35473
|
-
return !pointA ||
|
|
35474
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35475
|
-
coord[0] !== pointB.coord()[0] &&
|
|
35476
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35477
|
-
coord[0] !== pointC.coord()[0];
|
|
35478
|
-
}],
|
|
35479
|
-
onMove: onMoveHandler
|
|
35480
|
-
});
|
|
35481
|
-
|
|
35482
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35483
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35484
|
-
// to be executed synchronously, which causes this issue.
|
|
35485
|
-
let pointB = null;
|
|
35486
|
-
pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35487
|
-
coord: coords[1],
|
|
35488
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35489
|
-
return !pointB ||
|
|
35490
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35491
|
-
coord[0] !== pointA.coord()[0] &&
|
|
35492
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35493
|
-
coord[0] !== pointC.coord()[0];
|
|
35494
|
-
}],
|
|
35495
|
-
onMove: onMoveHandler
|
|
35496
|
-
});
|
|
35497
|
-
|
|
35498
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35499
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35500
|
-
// to be executed synchronously, which causes this issue.
|
|
35501
|
-
let pointC = null;
|
|
35502
|
-
pointC = this.pointC = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35503
|
-
coord: coords[2],
|
|
35504
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35505
|
-
return !pointC ||
|
|
35506
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35507
|
-
coord[0] !== pointA.coord()[0] &&
|
|
35508
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35509
|
-
coord[0] !== pointB.coord()[0];
|
|
35510
|
-
}],
|
|
35511
|
-
onMove: onMoveHandler
|
|
35512
|
-
});
|
|
35513
|
-
this.updateQuadratic();
|
|
35514
|
-
};
|
|
35515
|
-
updateQuadratic = () => {
|
|
35516
|
-
const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(this.props);
|
|
35517
|
-
if (!coeffs) {
|
|
35518
|
-
return;
|
|
35519
|
-
}
|
|
35520
|
-
|
|
35521
|
-
// Extract coefficients the parabola
|
|
35522
|
-
const a = coeffs[0];
|
|
35523
|
-
const b = coeffs[1];
|
|
35524
|
-
const c = coeffs[2];
|
|
35525
|
-
|
|
35526
|
-
// Plot and style
|
|
35527
|
-
if (this.parabola) {
|
|
35528
|
-
const path = this.graphie?.svgParabolaPath(a, b, c);
|
|
35529
|
-
this.parabola?.attr({
|
|
35530
|
-
path: path
|
|
35531
|
-
});
|
|
35532
|
-
} else {
|
|
35533
|
-
this.parabola = this.graphie?.parabola(a, b, c);
|
|
35534
|
-
this.parabola?.attr({
|
|
35535
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.DYNAMIC,
|
|
35536
|
-
...this._lineStroke()
|
|
35537
|
-
});
|
|
35538
|
-
this.parabola?.toBack();
|
|
35539
|
-
}
|
|
35540
|
-
};
|
|
35541
|
-
removeQuadraticControls = () => {
|
|
35542
|
-
this.pointA?.remove();
|
|
35543
|
-
this.pointB?.remove();
|
|
35544
|
-
this.pointC?.remove();
|
|
35545
|
-
if (this.parabola) {
|
|
35546
|
-
this.parabola.remove();
|
|
35547
|
-
this.parabola = null;
|
|
35548
|
-
}
|
|
35549
|
-
};
|
|
35550
|
-
addSinusoidControls = () => {
|
|
35551
|
-
if (this.props.graph.type !== "sinusoid") {
|
|
35552
|
-
throw makeInvalidTypeError("addSinusoidControls", "sinusoid");
|
|
35553
|
-
}
|
|
35554
|
-
let coords = this.props.graph.coords;
|
|
35555
|
-
if (!coords) {
|
|
35556
|
-
coords = InteractiveGraph.defaultSinusoidCoords(this.props);
|
|
35557
|
-
}
|
|
35558
|
-
const onMoveHandler = () => {
|
|
35559
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
35560
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35561
|
-
coords: [pointA.coord(), pointB.coord()]
|
|
35562
|
-
});
|
|
35563
|
-
this.onChange({
|
|
35564
|
-
graph: graph
|
|
35565
|
-
});
|
|
35566
|
-
this.updateSinusoid();
|
|
35567
|
-
};
|
|
35568
|
-
|
|
35569
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35570
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35571
|
-
// to be executed synchronously, which causes this issue.
|
|
35572
|
-
let pointA = null;
|
|
35573
|
-
pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35574
|
-
coord: coords[0],
|
|
35575
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35576
|
-
return !pointA || coord[0] !== pointB.coord()[0];
|
|
35577
|
-
}],
|
|
35578
|
-
onMove: onMoveHandler
|
|
35579
|
-
});
|
|
35580
|
-
const pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35581
|
-
coord: coords[1],
|
|
35582
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35583
|
-
// @ts-expect-error - TS2339 - Property 'coord' does not exist on type 'never'.
|
|
35584
|
-
return !pointA || coord[0] !== pointA.coord()[0];
|
|
35585
|
-
}],
|
|
35586
|
-
onMove: onMoveHandler
|
|
35587
|
-
});
|
|
35588
|
-
this.updateSinusoid();
|
|
35589
|
-
};
|
|
35590
|
-
updateSinusoid = () => {
|
|
35591
|
-
const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(this.props);
|
|
35592
|
-
if (!coeffs) {
|
|
35593
|
-
return;
|
|
35594
|
-
}
|
|
35595
|
-
const a = coeffs[0];
|
|
35596
|
-
const b = coeffs[1];
|
|
35597
|
-
const c = coeffs[2];
|
|
35598
|
-
const d = coeffs[3];
|
|
35599
|
-
|
|
35600
|
-
// Plot and style
|
|
35601
|
-
if (this.sinusoid) {
|
|
35602
|
-
const path = this.graphie?.svgSinusoidPath(a, b, c, d);
|
|
35603
|
-
this.sinusoid?.attr({
|
|
35604
|
-
path: path
|
|
35605
|
-
});
|
|
35606
|
-
} else {
|
|
35607
|
-
this.sinusoid = this.graphie?.sinusoid(a, b, c, d);
|
|
35608
|
-
this.sinusoid?.attr({
|
|
35609
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.DYNAMIC,
|
|
35610
|
-
...this._lineStroke()
|
|
35611
|
-
});
|
|
35612
|
-
this.sinusoid?.toBack();
|
|
35613
|
-
}
|
|
35614
|
-
};
|
|
35615
|
-
removeSinusoidControls = () => {
|
|
35616
|
-
this.pointA?.remove();
|
|
35617
|
-
this.pointB?.remove();
|
|
35618
|
-
if (this.sinusoid) {
|
|
35619
|
-
this.sinusoid.remove();
|
|
35620
|
-
this.sinusoid = null;
|
|
35621
|
-
}
|
|
35622
|
-
};
|
|
35623
|
-
addCircleControls = () => {
|
|
35624
|
-
const graphie = this.graphie;
|
|
35625
|
-
const minSnap = ___default["default"].min(graphie.snap);
|
|
35626
|
-
const circle = this.circle = graphie?.addCircleGraph({
|
|
35627
|
-
// @ts-expect-error - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'.
|
|
35628
|
-
center: this.props.graph.center || [0, 0],
|
|
35629
|
-
// @ts-expect-error - TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
35630
|
-
radius: this.props.graph.radius || ___default["default"].min(this.props.step),
|
|
35631
|
-
snapX: graphie.snap[0],
|
|
35632
|
-
snapY: graphie.snap[1],
|
|
35633
|
-
minRadius: minSnap,
|
|
35634
|
-
snapRadius: minSnap
|
|
35635
|
-
});
|
|
35636
|
-
$__default["default"](circle).on("move", () => {
|
|
35637
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
35638
|
-
center: circle.center,
|
|
35639
|
-
radius: circle.radius
|
|
35640
|
-
});
|
|
35641
|
-
this.onChange({
|
|
35642
|
-
graph: graph
|
|
35643
|
-
});
|
|
35644
|
-
});
|
|
35645
|
-
};
|
|
35646
|
-
removeCircleControls = () => {
|
|
35647
|
-
this.circle?.remove();
|
|
35648
|
-
};
|
|
35649
|
-
addLinearSystemControls = () => {
|
|
35650
|
-
const graphie = this.graphie;
|
|
35651
|
-
const coords = InteractiveGraph.getLinearSystemCoords(this.props.graph, this.props);
|
|
35652
|
-
const segmentColors = [KhanColors.INTERACTIVE, KhanColors.GREEN];
|
|
35653
|
-
const points = this.points = ___default["default"].map(coords, (segmentCoords, segmentIndex) => {
|
|
35654
|
-
// NOTE(jeresig): This code attempts to access the variable
|
|
35655
|
-
// while it's still being defined, it should be refactored!
|
|
35656
|
-
// The callbacks appear to be executed synchronously, which
|
|
35657
|
-
// causes this issue.
|
|
35658
|
-
const segmentPoints = [];
|
|
35659
|
-
for (let i = 0; i < segmentCoords.length; i += 1) {
|
|
35660
|
-
const coord = segmentCoords[i];
|
|
35661
|
-
segmentPoints.push(Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35662
|
-
coord: coord,
|
|
35663
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
35664
|
-
const otherSegment = segmentPoints[1 - i];
|
|
35665
|
-
if (!otherSegment) {
|
|
35666
|
-
// segment hasn't been defined yet
|
|
35667
|
-
// because we're still creating them
|
|
35668
|
-
return;
|
|
35669
|
-
}
|
|
35670
|
-
return !kmath.point.equal(coord, otherSegment.coord());
|
|
35671
|
-
}],
|
|
35672
|
-
onMove: () => {
|
|
35673
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
35674
|
-
coords: ___default["default"].map(this.points, segment => ___default["default"].invoke(segment, "coord"))
|
|
35675
|
-
});
|
|
35676
|
-
this.onChange({
|
|
35677
|
-
graph: graph
|
|
35678
|
-
});
|
|
35679
|
-
},
|
|
35680
|
-
normalStyle: {
|
|
35681
|
-
fill: segmentColors[segmentIndex]
|
|
35682
|
-
},
|
|
35683
|
-
highlightStyle: {
|
|
35684
|
-
fill: segmentColors[segmentIndex]
|
|
35685
|
-
}
|
|
35686
|
-
}));
|
|
35687
|
-
}
|
|
35688
|
-
return segmentPoints;
|
|
35689
|
-
});
|
|
35690
|
-
this.lines = ___default["default"].map(points, (segmentPoints, segmentIndex) => {
|
|
35691
|
-
return Interactive2.addMovableLine(graphie, {
|
|
35692
|
-
points: segmentPoints,
|
|
35693
|
-
static: true,
|
|
35694
|
-
extendLine: true,
|
|
35695
|
-
normalStyle: {
|
|
35696
|
-
stroke: segmentColors[segmentIndex]
|
|
35697
|
-
}
|
|
35698
|
-
});
|
|
35699
|
-
});
|
|
35700
|
-
};
|
|
35701
|
-
removeLinearSystemControls = () => {
|
|
35702
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
35703
|
-
___default["default"].invoke(this.lines, "remove");
|
|
35704
|
-
___default["default"].map(this.points, segment => ___default["default"].invoke(segment, "remove"));
|
|
35705
|
-
};
|
|
35706
|
-
isCoordInTrash = coord => {
|
|
35707
|
-
if (this.props.apiOptions.isMobile) {
|
|
35708
|
-
return false;
|
|
35709
|
-
}
|
|
35710
|
-
const graphie = this.graphie;
|
|
35711
|
-
const screenPoint = graphie?.scalePoint(coord);
|
|
35712
|
-
return screenPoint[0] >= graphie.xpixels - 40 && screenPoint[1] >= graphie.ypixels - 40;
|
|
35713
|
-
};
|
|
35714
|
-
createPointForPointsType = (coord, i) => {
|
|
35715
|
-
const self = this;
|
|
35716
|
-
const remove = () => {
|
|
35717
|
-
self.points = ___default["default"].filter(self.points, function (pt) {
|
|
35718
|
-
return pt !== point;
|
|
35719
|
-
});
|
|
35720
|
-
// update the correct answer box
|
|
35721
|
-
self.updateCoordsFromPoints();
|
|
35722
|
-
|
|
35723
|
-
// remove this movablePoint from graphie.
|
|
35724
|
-
// we wait to do this until we're not inside of
|
|
35725
|
-
// said point's onMoveEnd method so its state is
|
|
35726
|
-
// consistent throughout this method call
|
|
35727
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35728
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
35729
|
-
setTimeout(point.remove.bind(point), 0); // eslint-disable-line no-restricted-syntax
|
|
35730
|
-
};
|
|
35731
|
-
|
|
35732
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
35733
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
35734
|
-
// to be executed synchronously, which causes this issue.
|
|
35735
|
-
let point = null;
|
|
35736
|
-
point = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
35737
|
-
coord: coord,
|
|
35738
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), function (coord) {
|
|
35739
|
-
// TODO(jack): There ought to be a
|
|
35740
|
-
// MovablePoint.constraints.avoid
|
|
35741
|
-
// default that lets you do things like this
|
|
35742
|
-
return ___default["default"].all(self.points, function (pt) {
|
|
35743
|
-
return point === pt || !kmath.point.equal(coord, pt.coord());
|
|
35744
|
-
});
|
|
35745
|
-
}],
|
|
35746
|
-
onMoveStart: function () {
|
|
35747
|
-
if (self.isClickToAddPoints()) {
|
|
35748
|
-
self.setTrashCanVisibility(1);
|
|
35749
|
-
}
|
|
35750
|
-
},
|
|
35751
|
-
onMove: self.updateCoordsFromPoints,
|
|
35752
|
-
onMoveEnd: function (coord) {
|
|
35753
|
-
if (self.isClickToAddPoints()) {
|
|
35754
|
-
if (self.isCoordInTrash(coord)) {
|
|
35755
|
-
remove();
|
|
35756
|
-
}
|
|
35757
|
-
// In case we mouseup'd off the graphie and that
|
|
35758
|
-
// stopped the move (in which case, we might not
|
|
35759
|
-
// be in isCoordInTrash()
|
|
35760
|
-
self.setTrashCanVisibility(0.5);
|
|
35761
|
-
}
|
|
35762
|
-
},
|
|
35763
|
-
...(this.props.apiOptions.isMobile && self.isClickToAddPoints() ? {
|
|
35764
|
-
onRemove: remove
|
|
35765
|
-
} : {})
|
|
35766
|
-
});
|
|
35767
|
-
return point;
|
|
35768
|
-
};
|
|
35769
|
-
removePoint = point => {
|
|
35770
|
-
let index = null;
|
|
35771
|
-
this.points = ___default["default"].filter(this.points, function (pt, i) {
|
|
35772
|
-
if (pt === point) {
|
|
35773
|
-
// @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'null'.
|
|
35774
|
-
index = i;
|
|
35775
|
-
return false;
|
|
35776
|
-
}
|
|
35777
|
-
return true;
|
|
35778
|
-
});
|
|
35779
|
-
return index;
|
|
35780
|
-
};
|
|
35781
|
-
createPointForPolygonType = (coord, i) => {
|
|
35782
|
-
if (this.props.graph.type !== "polygon") {
|
|
35783
|
-
throw makeInvalidTypeError("createPointForPolygonType", "polygon");
|
|
35784
|
-
}
|
|
35785
|
-
|
|
35786
|
-
// TODO(alex): check against "grid" instead, use constants
|
|
35787
|
-
const snapToGrid = !___default["default"].contains(["angles", "sides"], this.props.graph.snapTo);
|
|
35788
|
-
|
|
35789
|
-
// Index relative to current point -> absolute index
|
|
35790
|
-
// NOTE: This does not work when isClickToAddPoints() == true,
|
|
35791
|
-
// as `i` can be changed by dragging a point to the trash
|
|
35792
|
-
// Currently this function is only called when !isClickToAddPoints()
|
|
35793
|
-
const rel = j => {
|
|
35794
|
-
return (i + j + this.points.length) % this.points.length;
|
|
35795
|
-
};
|
|
35796
|
-
const remove = () => {
|
|
35797
|
-
// remove this point from points
|
|
35798
|
-
const index = this.removePoint(point);
|
|
35799
|
-
if (this.polygon?.closed()) {
|
|
35800
|
-
// We should be checking if this.points is defined before rotating them.
|
|
35801
|
-
// @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'.
|
|
35802
|
-
this.points = rotate(this.points, index);
|
|
35803
|
-
this.polygon?.update({
|
|
35804
|
-
closed: false
|
|
35805
|
-
});
|
|
35806
|
-
}
|
|
35807
|
-
this.updatePolygon();
|
|
35808
|
-
// the polygon is now unclosed, so we need to
|
|
35809
|
-
// remove any points props
|
|
35810
|
-
this.clearCoords();
|
|
35811
|
-
|
|
35812
|
-
// remove this movablePoint from graphie.
|
|
35813
|
-
// wait to do this until we're not inside of
|
|
35814
|
-
// said point's onMoveEnd method so state is
|
|
35815
|
-
// consistent throughout the method call
|
|
35816
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35817
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35818
|
-
setTimeout(point.remove.bind(point), 0);
|
|
35819
|
-
};
|
|
35820
|
-
const setInitialMove = debounce__default["default"]((point, bool) => {
|
|
35821
|
-
point.state.isInitialMove = bool;
|
|
35822
|
-
}, 1000);
|
|
35823
|
-
const onMoveEndHandler = coord => {
|
|
35824
|
-
// If the user clicked and dragged a point over endpoint,
|
|
35825
|
-
// join the them
|
|
35826
|
-
const overlappedPoints = this.points.length > 1 && (point === this.points[0] && kmath.point.equal(coord, ___default["default"].last(this.points).coord()) || point === ___default["default"].last(this.points) && kmath.point.equal(coord, this.points[0].coord()));
|
|
35827
|
-
if (this.isClickToAddPoints()) {
|
|
35828
|
-
if (this.isCoordInTrash(coord)) {
|
|
35829
|
-
remove();
|
|
35830
|
-
} else if (overlappedPoints) {
|
|
35831
|
-
const pointToRemove = this.points.pop();
|
|
35832
|
-
if (this.points.length > 2) {
|
|
35833
|
-
this.polygon?.update({
|
|
35834
|
-
closed: true
|
|
35835
|
-
});
|
|
35836
|
-
this.updateCoordsFromPoints();
|
|
35837
|
-
} else {
|
|
35838
|
-
this.polygon?.update({
|
|
35839
|
-
closed: false
|
|
35840
|
-
});
|
|
35841
|
-
this.clearCoords();
|
|
35842
|
-
}
|
|
35843
|
-
this.updatePolygon();
|
|
35844
|
-
// remove this movablePoint from graphie.
|
|
35845
|
-
// wait to do this until we're not inside of
|
|
35846
|
-
// said point's onMoveEnd method so state is
|
|
35847
|
-
// consistent throughout the method call
|
|
35848
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35849
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35850
|
-
setTimeout(pointToRemove.remove.bind(pointToRemove), 0);
|
|
35851
|
-
} else {
|
|
35852
|
-
// If the user clicked and dragged a point over any other
|
|
35853
|
-
// existing point, fix shape
|
|
35854
|
-
const shouldRemove = ___default["default"].any(this.points, function (pt) {
|
|
35855
|
-
return pt !== point && kmath.point.equal(pt.coord(), coord);
|
|
35856
|
-
});
|
|
35857
|
-
if (shouldRemove) {
|
|
35858
|
-
this.removePoint(point);
|
|
35859
|
-
if (this.points.length < 3) {
|
|
35860
|
-
this.polygon?.update({
|
|
35861
|
-
closed: false
|
|
35862
|
-
});
|
|
35863
|
-
this.clearCoords();
|
|
35864
|
-
} else if (this.polygon?.closed()) {
|
|
35865
|
-
this.updateCoordsFromPoints();
|
|
35866
|
-
}
|
|
35867
|
-
this.updatePolygon();
|
|
35868
|
-
// remove this movablePoint from graphie.
|
|
35869
|
-
// wait to do this until we're not inside
|
|
35870
|
-
// said point's onMoveEnd method so state
|
|
35871
|
-
// is consistent throughout the method call
|
|
35872
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
35873
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
35874
|
-
setTimeout(point.remove.bind(point), 0);
|
|
35875
|
-
} else {
|
|
35876
|
-
// If this was
|
|
35877
|
-
// * a click on the first or last point
|
|
35878
|
-
// * and not a deletion
|
|
35879
|
-
// * and not a drag,
|
|
35880
|
-
// * and not a creation of a new point
|
|
35881
|
-
// (see !point.state.isInitialMove, below),
|
|
35882
|
-
// * and our polygon is not closed,
|
|
35883
|
-
// * and we can close it (we need at least the same
|
|
35884
|
-
// number of points as coordinates asked for in the
|
|
35885
|
-
// question),
|
|
35886
|
-
// then close it
|
|
35887
|
-
const isFirstOrLastPoint = point === ___default["default"].head(this.points) || point === ___default["default"].last(this.points);
|
|
35888
|
-
const pointNotMoved = !point.hasMoved();
|
|
35889
|
-
// NOTE (jsatk): Currently different on mobile versus
|
|
35890
|
-
// destkop
|
|
35891
|
-
const notANewPoint = !point.state.isInitialMove;
|
|
35892
|
-
const polygonNotClosed = !this.polygon?.closed();
|
|
35893
|
-
const allPointsPlotted = this.points.length > 2;
|
|
35894
|
-
const canCloseUnclosedPolygon = isFirstOrLastPoint && pointNotMoved && notANewPoint && polygonNotClosed && allPointsPlotted;
|
|
35895
|
-
if (canCloseUnclosedPolygon) {
|
|
35896
|
-
this.polygon?.update({
|
|
35897
|
-
closed: true
|
|
35898
|
-
});
|
|
35899
|
-
this.updatePolygon();
|
|
35900
|
-
|
|
35901
|
-
// We finally have a closed polygon, so save our
|
|
35902
|
-
// points to props
|
|
35903
|
-
this.updateCoordsFromPoints();
|
|
35904
|
-
}
|
|
35905
|
-
}
|
|
35906
|
-
}
|
|
35907
|
-
|
|
35908
|
-
// In case we mouseup'd off the graphie and that
|
|
35909
|
-
// stopped the move
|
|
35910
|
-
this.setTrashCanVisibility(0.5);
|
|
35911
|
-
}
|
|
35912
|
-
setInitialMove(point, false);
|
|
35913
|
-
};
|
|
35914
|
-
const graphConstraint = coord => {
|
|
35915
|
-
// These constraints are all relative to the other points, so if
|
|
35916
|
-
// we're creating the initial points and haven't added any others
|
|
35917
|
-
// to the graph, we can't enforce them.
|
|
35918
|
-
if (this.points == null || this.points.length === 0) {
|
|
35919
|
-
return true;
|
|
35920
|
-
}
|
|
35921
|
-
const coords = ___default["default"].invoke(this.points, "coord");
|
|
35922
|
-
coords[i] = coord;
|
|
35923
|
-
|
|
35924
|
-
// Check for invalid positioning, but only if we aren't adding
|
|
35925
|
-
// points one click at a time, since those added points could
|
|
35926
|
-
// have already violated these constraints
|
|
35927
|
-
if (!this.isClickToAddPoints()) {
|
|
35928
|
-
// Polygons can't have consecutive collinear points
|
|
35929
|
-
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)])) {
|
|
35930
|
-
return false;
|
|
35931
|
-
}
|
|
35932
|
-
const segments = ___default["default"].zip(coords, rotate(coords));
|
|
35933
|
-
if (this.points.length > 3) {
|
|
35934
|
-
// Constrain to simple (non self-intersecting) polygon by
|
|
35935
|
-
// testing whether adjacent segments intersect any others
|
|
35936
|
-
for (let j = -1; j <= 0; j++) {
|
|
35937
|
-
const segment = segments[rel(j)];
|
|
35938
|
-
const others = ___default["default"].without(segments, segment, segments[rel(j - 1)], segments[rel(j + 1)]);
|
|
35939
|
-
for (let k = 0; k < others.length; k++) {
|
|
35940
|
-
const other = others[k];
|
|
35941
|
-
// @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Line'.
|
|
35942
|
-
if (intersects(segment, other)) {
|
|
35943
|
-
return false;
|
|
35944
|
-
}
|
|
35945
|
-
}
|
|
35946
|
-
}
|
|
35947
|
-
}
|
|
35948
|
-
}
|
|
35949
|
-
if (
|
|
35950
|
-
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
35951
|
-
this.props.graph.snapTo === "angles" && this.points.length > 2) {
|
|
35952
|
-
// Snap to whole degree interior angles
|
|
35953
|
-
|
|
35954
|
-
const angles = ___default["default"].map(angleMeasures(coords), function (rad) {
|
|
35955
|
-
return rad * 180 / Math.PI;
|
|
35956
|
-
});
|
|
35957
|
-
___default["default"].each([-1, 1], function (j) {
|
|
35958
|
-
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
35959
|
-
});
|
|
35960
|
-
const getAngle = function (a, vertex, b) {
|
|
35961
|
-
const angle = GraphUtils.findAngleDeprecated(coords[rel(a)], coords[rel(b)], coords[rel(vertex)]);
|
|
35962
|
-
return (angle + 360) % 360;
|
|
35963
|
-
};
|
|
35964
|
-
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
35965
|
-
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
35966
|
-
|
|
35967
|
-
// Avoid degenerate triangles
|
|
35968
|
-
if (___default["default"].any(innerAngles, function (angle) {
|
|
35969
|
-
return leq(angle, 1);
|
|
35970
|
-
})) {
|
|
35971
|
-
return false;
|
|
35972
|
-
}
|
|
35973
|
-
const knownSide = magnitude(
|
|
35974
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
35975
|
-
vector(coords[rel(-1)], coords[rel(1)]));
|
|
35976
|
-
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
35977
|
-
|
|
35978
|
-
// Solve for side by using the law of sines
|
|
35979
|
-
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
35980
|
-
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
35981
|
-
const offset = polar$2(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
35982
|
-
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
35983
|
-
}
|
|
35984
|
-
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
35985
|
-
if (this.props.graph.snapTo === "sides" && this.points.length > 1) {
|
|
35986
|
-
// Snap to whole unit side measures
|
|
35987
|
-
|
|
35988
|
-
const sides = ___default["default"].map([[coords[rel(-1)], coords[i]], [coords[i], coords[rel(1)]], [coords[rel(-1)], coords[rel(1)]]], function (coords) {
|
|
35989
|
-
// @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.
|
|
35990
|
-
return magnitude(vector(...coords));
|
|
35991
|
-
});
|
|
35992
|
-
___default["default"].each([0, 1], function (j) {
|
|
35993
|
-
sides[j] = Math.round(sides[j]);
|
|
35994
|
-
});
|
|
35995
|
-
|
|
35996
|
-
// Avoid degenerate triangles
|
|
35997
|
-
if (leq(sides[1] + sides[2], sides[0]) || leq(sides[0] + sides[2], sides[1]) || leq(sides[0] + sides[1], sides[2])) {
|
|
35998
|
-
return false;
|
|
35999
|
-
}
|
|
36000
|
-
|
|
36001
|
-
// Solve for angle by using the law of cosines
|
|
36002
|
-
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
36003
|
-
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
36004
|
-
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
36005
|
-
const offset = polar$2(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
36006
|
-
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
36007
|
-
}
|
|
36008
|
-
// Snap to grid (already done)
|
|
36009
|
-
return true;
|
|
36010
|
-
};
|
|
36011
|
-
const point = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
36012
|
-
coord: coord,
|
|
36013
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), snapToGrid ? Interactive2.MovablePoint.constraints.snap() : null, graphConstraint],
|
|
36014
|
-
onMoveStart: () => {
|
|
36015
|
-
if (this.isClickToAddPoints()) {
|
|
36016
|
-
this.setTrashCanVisibility(1);
|
|
36017
|
-
}
|
|
36018
|
-
},
|
|
36019
|
-
onMove: () => {
|
|
36020
|
-
if (this.polygon?.closed()) {
|
|
36021
|
-
this.updateCoordsFromPoints();
|
|
36022
|
-
}
|
|
36023
|
-
},
|
|
36024
|
-
onMoveEnd: onMoveEndHandler,
|
|
36025
|
-
...(this.props.apiOptions.isMobile && this.isClickToAddPoints() ? {
|
|
36026
|
-
onRemove: remove
|
|
36027
|
-
} : {})
|
|
36028
|
-
});
|
|
36029
|
-
point.state.isInitialMove = true;
|
|
36030
|
-
return point;
|
|
36031
|
-
};
|
|
36032
|
-
updateCoordsFromPoints = () => {
|
|
36033
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
36034
|
-
// Handle old movable points with .coord, or
|
|
36035
|
-
// Interactive2.MovablePoint's with .coord()
|
|
36036
|
-
coords: ___default["default"].map(this.points, function (point) {
|
|
36037
|
-
return ___default["default"].result(point, "coord");
|
|
36038
|
-
})
|
|
36039
|
-
});
|
|
36040
|
-
this.onChange({
|
|
36041
|
-
graph: graph
|
|
36042
|
-
});
|
|
36043
|
-
};
|
|
36044
|
-
clearCoords = () => {
|
|
36045
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
36046
|
-
coords: null
|
|
36047
|
-
});
|
|
36048
|
-
this.onChange({
|
|
36049
|
-
graph: graph
|
|
36050
|
-
});
|
|
36051
|
-
};
|
|
36052
|
-
onChange = data => {
|
|
36053
|
-
this.props.onChange(data);
|
|
36054
|
-
this.props.trackInteraction();
|
|
36055
|
-
};
|
|
36056
|
-
addPointControls = () => {
|
|
36057
|
-
if (this.props.graph.type !== "point") {
|
|
36058
|
-
throw makeInvalidTypeError("addPointControls", "point");
|
|
36059
|
-
}
|
|
36060
|
-
const coords = InteractiveGraph.getPointCoords(this.props.graph, this.props);
|
|
36061
|
-
// Clear out our old points so that newly added points don't
|
|
36062
|
-
// "collide" with them and reposition when being added
|
|
36063
|
-
// Without this, when added, each point checks whether it is on top
|
|
36064
|
-
// of a point in this.points, which (a) shouldn't matter since
|
|
36065
|
-
// we're clearing out this.points anyways, and (b) can cause problems
|
|
36066
|
-
// if each of this.points is a MovablePoint instead of an
|
|
36067
|
-
// Interactive2.MovablePoint, since one has a .coord and the other
|
|
36068
|
-
// has .coord()
|
|
36069
|
-
// TODO(jack): Figure out a better way to do this
|
|
36070
|
-
this.points = [];
|
|
36071
|
-
this.points = ___default["default"].map(coords, this.createPointForPointsType, this);
|
|
36072
|
-
};
|
|
36073
|
-
removePointControls = () => {
|
|
36074
|
-
___default["default"].invoke(this.points, "remove");
|
|
36075
|
-
};
|
|
36076
|
-
addSegmentControls = () => {
|
|
36077
|
-
if (this.props.graph.type !== "segment") {
|
|
36078
|
-
throw makeInvalidTypeError("addSegmentControls", "segment");
|
|
36079
|
-
}
|
|
36080
|
-
const self = this;
|
|
36081
|
-
const graphie = this.graphie;
|
|
36082
|
-
const coords = InteractiveGraph.getSegmentCoords(this.props.graph, this.props);
|
|
36083
|
-
const createPoint = options => Interactive2.addMaybeMobileMovablePoint(this, options);
|
|
36084
|
-
this.points = [];
|
|
36085
|
-
this.lines = ___default["default"].map(coords, function (segment, i) {
|
|
36086
|
-
const updateCoordProps = function () {
|
|
36087
|
-
const graph = ___default["default"].extend({}, self.props.graph, {
|
|
36088
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
36089
|
-
coords: ___default["default"].invoke(self.lines, "coords")
|
|
36090
|
-
});
|
|
36091
|
-
self.onChange({
|
|
36092
|
-
graph: graph
|
|
36093
|
-
});
|
|
36094
|
-
};
|
|
36095
|
-
|
|
36096
|
-
// NOTE(jeresig): This code attempts to access the variable
|
|
36097
|
-
// while it's still being defined, it should be refactored!
|
|
36098
|
-
// The callbacks appear to be executed synchronously, which
|
|
36099
|
-
// causes this issue.
|
|
36100
|
-
const points = [];
|
|
36101
|
-
for (let i = 0; i < segment.length; i += 1) {
|
|
36102
|
-
const coord = segment[i];
|
|
36103
|
-
points.push(createPoint({
|
|
36104
|
-
coord: coord,
|
|
36105
|
-
constraints: [Interactive2.MovablePoint.constraints.bound(), Interactive2.MovablePoint.constraints.snap(), coord => {
|
|
36106
|
-
const otherPoint = points[1 - i];
|
|
36107
|
-
if (!otherPoint) {
|
|
36108
|
-
// point hasn't been defined yet
|
|
36109
|
-
// because we're still creating them
|
|
36110
|
-
// We return false as the points aren't
|
|
36111
|
-
// going to be in the same position at
|
|
36112
|
-
// this point.
|
|
36113
|
-
return false;
|
|
36114
|
-
}
|
|
36115
|
-
return !kmath.point.equal(coord, otherPoint.coord());
|
|
36116
|
-
}],
|
|
36117
|
-
onMove: updateCoordProps
|
|
36118
|
-
}));
|
|
36119
|
-
}
|
|
36120
|
-
self.points = self.points.concat(points);
|
|
36121
|
-
const line = Interactive2.addMovableLine(graphie, {
|
|
36122
|
-
points: points,
|
|
36123
|
-
static: false,
|
|
36124
|
-
constraints: [
|
|
36125
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
36126
|
-
Interactive2.MovableLine.constraints.bound(),
|
|
36127
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
36128
|
-
Interactive2.MovableLine.constraints.snap()],
|
|
36129
|
-
onMove: [
|
|
36130
|
-
// @ts-expect-error - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
36131
|
-
Interactive2.MovableLine.onMove.updatePoints, updateCoordProps],
|
|
36132
|
-
normalStyle: {
|
|
36133
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36134
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE,
|
|
36135
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36136
|
-
...this._lineStroke()
|
|
36137
|
-
},
|
|
36138
|
-
highlightStyle: {
|
|
36139
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36140
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTING,
|
|
36141
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36142
|
-
...this._lineStroke()
|
|
36143
|
-
}
|
|
36144
|
-
});
|
|
36145
|
-
___default["default"].invoke(points, "toFront");
|
|
36146
|
-
return line;
|
|
36147
|
-
}, this);
|
|
36148
|
-
};
|
|
36149
|
-
removeSegmentControls = () => {
|
|
36150
|
-
___default["default"].invoke(this.points, "remove");
|
|
36151
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
36152
|
-
___default["default"].invoke(this.lines, "remove");
|
|
36153
|
-
};
|
|
36154
|
-
addRayControls = () => {
|
|
36155
|
-
this.addLine("ray");
|
|
36156
|
-
};
|
|
36157
|
-
removeRayControls = () => {
|
|
36158
|
-
this.removeLine();
|
|
36159
|
-
};
|
|
36160
|
-
addPolygonControls = () => {
|
|
36161
|
-
this.polygon = null;
|
|
36162
|
-
const coords = InteractiveGraph.getPolygonCoords(this.props.graph, this.props);
|
|
36163
|
-
// Clear out our old points so that newly added points don't
|
|
36164
|
-
// "collide", as in `addPointControls`
|
|
36165
|
-
this.points = [];
|
|
36166
|
-
this.points = ___default["default"].map(coords, this.createPointForPolygonType, this);
|
|
36167
|
-
this.updatePolygon();
|
|
36168
|
-
};
|
|
36169
|
-
updatePolygon = () => {
|
|
36170
|
-
if (this.props.graph.type !== "polygon") {
|
|
36171
|
-
throw makeInvalidTypeError("updatePolygon", "polygon");
|
|
36172
|
-
}
|
|
36173
|
-
let closed;
|
|
36174
|
-
if (this.polygon) {
|
|
36175
|
-
closed = this.polygon.closed();
|
|
36176
|
-
} else if (this.points.length >= 3) {
|
|
36177
|
-
closed = true;
|
|
36178
|
-
} else {
|
|
36179
|
-
// There will only be fewer than 3 points in click-to-add-vertices
|
|
36180
|
-
// mode, so we don't need to explicitly check for that here.
|
|
36181
|
-
closed = false;
|
|
36182
|
-
}
|
|
36183
|
-
const graphie = this.graphie;
|
|
36184
|
-
const n = this.points.length;
|
|
36185
|
-
|
|
36186
|
-
// TODO(alex): check against "grid" instead, use constants
|
|
36187
|
-
const snapToGrid = !___default["default"].contains(["angles", "sides"], this.props.graph.snapTo);
|
|
36188
|
-
const angleLabels = ___default["default"].times(n, function (i) {
|
|
36189
|
-
if (
|
|
36190
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36191
|
-
!this.props.graph.showAngles || !closed && (i === 0 || i === n - 1)) {
|
|
36192
|
-
return "";
|
|
36193
|
-
}
|
|
36194
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36195
|
-
if (this.props.graph.snapTo === "angles") {
|
|
36196
|
-
return "$deg0";
|
|
36197
|
-
}
|
|
36198
|
-
return "$deg1";
|
|
36199
|
-
}, this);
|
|
36200
|
-
const showRightAngleMarkers = ___default["default"].times(n, function (i) {
|
|
36201
|
-
return closed || i !== 0 && i !== n - 1;
|
|
36202
|
-
}, this);
|
|
36203
|
-
const numArcs = ___default["default"].times(n, function (i) {
|
|
36204
|
-
if (
|
|
36205
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36206
|
-
this.props.graph.showAngles && (closed || i !== 0 && i !== n - 1)) {
|
|
36207
|
-
return 1;
|
|
36208
|
-
}
|
|
36209
|
-
return 0;
|
|
36210
|
-
}, this);
|
|
36211
|
-
const sideLabels = ___default["default"].times(n, function (i) {
|
|
36212
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36213
|
-
if (!this.props.graph.showSides || !closed && i === n - 1) {
|
|
36214
|
-
return "";
|
|
36215
|
-
}
|
|
36216
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36217
|
-
if (this.props.graph.snapTo === "sides") {
|
|
36218
|
-
return "$len0";
|
|
36219
|
-
}
|
|
36220
|
-
return "$len1";
|
|
36221
|
-
}, this);
|
|
36222
|
-
if (this.polygon == null) {
|
|
36223
|
-
const self = this;
|
|
36224
|
-
self.polygon = Interactive2.addMovablePolygon(graphie, {
|
|
36225
|
-
constraints: [
|
|
36226
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
36227
|
-
Interactive2.MovablePolygon.constraints.bound(), snapToGrid ?
|
|
36228
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
36229
|
-
Interactive2.MovablePolygon.constraints.snap() : null],
|
|
36230
|
-
closed: closed,
|
|
36231
|
-
points: self.points,
|
|
36232
|
-
angleLabels: angleLabels,
|
|
36233
|
-
showRightAngleMarkers: showRightAngleMarkers,
|
|
36234
|
-
numArcs: numArcs,
|
|
36235
|
-
sideLabels: sideLabels,
|
|
36236
|
-
onMove: [
|
|
36237
|
-
// @ts-expect-error - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
36238
|
-
Interactive2.MovablePolygon.onMove.updatePoints, function () {
|
|
36239
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
36240
|
-
if (this.closed()) {
|
|
36241
|
-
self.updateCoordsFromPoints();
|
|
36242
|
-
}
|
|
36243
|
-
}],
|
|
36244
|
-
normalStyle: {
|
|
36245
|
-
stroke: this.props.apiOptions.isMobile ? KhanColors.BLUE_C : KhanColors.INTERACTIVE,
|
|
36246
|
-
...this._lineStroke()
|
|
36247
|
-
}
|
|
36248
|
-
});
|
|
36249
|
-
} else {
|
|
36250
|
-
// We only need to pass in the properties that might've changed
|
|
36251
|
-
this.polygon.update({
|
|
36252
|
-
closed: closed,
|
|
36253
|
-
points: this.points,
|
|
36254
|
-
angleLabels: angleLabels,
|
|
36255
|
-
showRightAngleMarkers: showRightAngleMarkers,
|
|
36256
|
-
numArcs: numArcs,
|
|
36257
|
-
sideLabels: sideLabels
|
|
36258
|
-
});
|
|
36259
|
-
}
|
|
36260
|
-
};
|
|
36261
|
-
removePolygonControls = () => {
|
|
36262
|
-
___default["default"].invoke(this.points, "remove");
|
|
36263
|
-
this.polygon?.remove();
|
|
36264
|
-
};
|
|
36265
|
-
addAngleControls = () => {
|
|
36266
|
-
if (this.props.graph.type !== "angle") {
|
|
36267
|
-
throw makeInvalidTypeError("addAngleControls", "angle");
|
|
36268
|
-
}
|
|
36269
|
-
const graph = this.props.graph;
|
|
36270
|
-
const graphie = this.graphie;
|
|
36271
|
-
const coords = InteractiveGraph.getAngleCoords(this.props.graph, this.props);
|
|
36272
|
-
|
|
36273
|
-
// The vertex snaps to the grid, but the rays don't...
|
|
36274
|
-
this.points = coords.map(function (coord, i) {
|
|
36275
|
-
return graphie?.addMovablePoint(___default["default"].extend({
|
|
36276
|
-
coord: coord,
|
|
36277
|
-
normalStyle: {
|
|
36278
|
-
stroke: KhanColors.INTERACTIVE,
|
|
36279
|
-
fill: KhanColors.INTERACTIVE
|
|
36280
|
-
}
|
|
36281
|
-
}, i === 1 ? {
|
|
36282
|
-
snapX: graphie.snap[0],
|
|
36283
|
-
snapY: graphie.snap[1]
|
|
36284
|
-
} : {}));
|
|
36285
|
-
});
|
|
36286
|
-
|
|
36287
|
-
// ...they snap to whole-degree angles from the vertex.
|
|
36288
|
-
this.angle = graphie?.addMovableAngle({
|
|
36289
|
-
points: this.points,
|
|
36290
|
-
snapDegrees: graph.snapDegrees || 1,
|
|
36291
|
-
snapOffsetDeg: graph.angleOffsetDeg || 0,
|
|
36292
|
-
angleLabel: graph.showAngles ? "$deg0" : "",
|
|
36293
|
-
pushOut: 2,
|
|
36294
|
-
allowReflex: defaultVal(graph.allowReflexAngles, true)
|
|
36295
|
-
});
|
|
36296
|
-
$__default["default"](this.angle).on("move", () => {
|
|
36297
|
-
this.onChange({
|
|
36298
|
-
graph: {
|
|
36299
|
-
...graph,
|
|
36300
|
-
coords: this.angle?.getClockwiseCoords()
|
|
36301
|
-
}
|
|
36302
|
-
});
|
|
36303
|
-
});
|
|
36304
|
-
};
|
|
36305
|
-
removeAngleControls = () => {
|
|
36306
|
-
___default["default"].invoke(this.points, "remove");
|
|
36307
|
-
this.angle?.remove();
|
|
36308
|
-
};
|
|
36309
|
-
toggleShowSides = () => {
|
|
36310
|
-
if (this.props.graph.type !== "polygon") {
|
|
36311
|
-
throw makeInvalidTypeError("toggleShowSides", "polygon");
|
|
36312
|
-
}
|
|
36313
|
-
const graph = ___default["default"].extend({}, this.props.graph, {
|
|
36314
|
-
showSides: !this.props.graph.showSides
|
|
36315
|
-
});
|
|
36316
|
-
this.onChange({
|
|
36317
|
-
graph: graph
|
|
36318
|
-
});
|
|
36319
|
-
};
|
|
36320
|
-
getUserInput() {
|
|
36321
|
-
return InteractiveGraph.getUserInputFromProps(this.props);
|
|
36322
|
-
}
|
|
36323
|
-
focus = $__default["default"].noop;
|
|
36324
|
-
render() {
|
|
36325
|
-
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
36326
|
-
const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
36327
|
-
const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
36328
|
-
let instructions;
|
|
36329
|
-
// isClickToAddPoints() only applies to points and polygons
|
|
36330
|
-
if (this.isClickToAddPoints() && this.state.shouldShowInstructions) {
|
|
36331
|
-
if (this.props.graph.type === "point") {
|
|
36332
|
-
instructions = this.context.strings.addPoints;
|
|
36333
|
-
} else if (this.props.graph.type === "polygon") {
|
|
36334
|
-
instructions = this.context.strings.addVertices;
|
|
36335
|
-
}
|
|
36336
|
-
} else {
|
|
36337
|
-
instructions = undefined;
|
|
36338
|
-
}
|
|
36339
|
-
let onMouseDown;
|
|
36340
|
-
if (this.isClickToAddPoints()) {
|
|
36341
|
-
onMouseDown = this.handleAddPointsMouseDown;
|
|
36342
|
-
}
|
|
36343
|
-
const isMobile = this.props.apiOptions.isMobile;
|
|
36344
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
36345
|
-
className: "perseus-widget " + "perseus-widget-interactive-graph",
|
|
36346
|
-
style: {
|
|
36347
|
-
width: box[0],
|
|
36348
|
-
height: box[1]
|
|
36349
|
-
}
|
|
36350
|
-
}, /*#__PURE__*/React__namespace.createElement(Graph, {
|
|
36351
|
-
instructions: instructions
|
|
36352
|
-
// eslint-disable-next-line react/no-string-refs
|
|
36353
|
-
,
|
|
36354
|
-
ref: "graph",
|
|
36355
|
-
box: box,
|
|
36356
|
-
labels: this.props.labels,
|
|
36357
|
-
range: this.props.range,
|
|
36358
|
-
step: isMobile ? Util.constrainedTickStepsFromTickSteps(this.props.step, this.props.range) : this.props.step,
|
|
36359
|
-
gridStep: gridStep,
|
|
36360
|
-
snapStep: snapStep,
|
|
36361
|
-
markings: this.props.markings,
|
|
36362
|
-
backgroundImage: this.props.backgroundImage,
|
|
36363
|
-
showProtractor: this.props.showProtractor,
|
|
36364
|
-
onMouseDown: onMouseDown,
|
|
36365
|
-
onGraphieUpdated: this.setGraphie,
|
|
36366
|
-
setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable,
|
|
36367
|
-
isMobile: isMobile
|
|
36368
|
-
}));
|
|
36369
|
-
}
|
|
36370
|
-
}
|
|
36371
35532
|
class InteractiveGraph extends React__namespace.Component {
|
|
36372
35533
|
legacyGraphRef = /*#__PURE__*/React__namespace.createRef();
|
|
36373
35534
|
mafsRef = /*#__PURE__*/React__namespace.createRef();
|
|
@@ -36396,25 +35557,17 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
36396
35557
|
return getPromptJSON$b(this.props, this.getUserInput());
|
|
36397
35558
|
}
|
|
36398
35559
|
render() {
|
|
36399
|
-
|
|
36400
|
-
const
|
|
36401
|
-
|
|
36402
|
-
|
|
36403
|
-
|
|
36404
|
-
|
|
36405
|
-
|
|
36406
|
-
|
|
36407
|
-
|
|
36408
|
-
|
|
36409
|
-
|
|
36410
|
-
box: box,
|
|
36411
|
-
showTooltips: !!this.props.showTooltips,
|
|
36412
|
-
readOnly: this.props.apiOptions?.readOnly
|
|
36413
|
-
}));
|
|
36414
|
-
}
|
|
36415
|
-
return /*#__PURE__*/React__namespace.createElement(LegacyInteractiveGraph, _extends({
|
|
36416
|
-
ref: this.legacyGraphRef
|
|
36417
|
-
}, this.props));
|
|
35560
|
+
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
35561
|
+
const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
35562
|
+
const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
35563
|
+
return /*#__PURE__*/React__namespace.createElement(StatefulMafsGraph, _extends({}, this.props, {
|
|
35564
|
+
ref: this.mafsRef,
|
|
35565
|
+
gridStep: gridStep,
|
|
35566
|
+
snapStep: snapStep,
|
|
35567
|
+
box: box,
|
|
35568
|
+
showTooltips: !!this.props.showTooltips,
|
|
35569
|
+
readOnly: this.props.apiOptions?.readOnly
|
|
35570
|
+
}));
|
|
36418
35571
|
}
|
|
36419
35572
|
|
|
36420
35573
|
/**
|
|
@@ -36730,35 +35883,14 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
36730
35883
|
}
|
|
36731
35884
|
}
|
|
36732
35885
|
|
|
36733
|
-
// exported for testing
|
|
36734
|
-
function shouldUseMafs(mafsFlags, graph) {
|
|
36735
|
-
if (typeof mafsFlags === "boolean" || typeof mafsFlags === "undefined") {
|
|
36736
|
-
return false;
|
|
36737
|
-
}
|
|
36738
|
-
switch (graph.type) {
|
|
36739
|
-
case "none":
|
|
36740
|
-
return true;
|
|
36741
|
-
case "point":
|
|
36742
|
-
if (graph.numPoints === UNLIMITED) {
|
|
36743
|
-
return Boolean(mafsFlags["unlimited-point"]);
|
|
36744
|
-
}
|
|
36745
|
-
return Boolean(mafsFlags["point"]);
|
|
36746
|
-
case "polygon":
|
|
36747
|
-
if (graph.numSides === UNLIMITED) {
|
|
36748
|
-
return Boolean(mafsFlags["unlimited-polygon"]);
|
|
36749
|
-
}
|
|
36750
|
-
return Boolean(mafsFlags["polygon"]);
|
|
36751
|
-
default:
|
|
36752
|
-
return Boolean(mafsFlags[graph.type]);
|
|
36753
|
-
}
|
|
36754
|
-
}
|
|
36755
35886
|
// We don't need to change any of the original props for static mode
|
|
36756
35887
|
const staticTransform$3 = ___default["default"].identity;
|
|
36757
35888
|
var InteractiveGraph$1 = {
|
|
36758
35889
|
name: "interactive-graph",
|
|
36759
35890
|
displayName: "Interactive graph (Assessments only)",
|
|
36760
35891
|
widget: InteractiveGraph,
|
|
36761
|
-
staticTransform: staticTransform$3
|
|
35892
|
+
staticTransform: staticTransform$3,
|
|
35893
|
+
getPublicWidgetOptions: perseusCore.getInteractiveGraphPublicWidgetOptions
|
|
36762
35894
|
};
|
|
36763
35895
|
|
|
36764
35896
|
// TODO: can we use WB typography?
|
|
@@ -39047,7 +38179,8 @@ var Matrix$1 = {
|
|
|
39047
38179
|
widget: Matrix,
|
|
39048
38180
|
transform: propTransform$1,
|
|
39049
38181
|
staticTransform: staticTransform$2,
|
|
39050
|
-
isLintable: true
|
|
38182
|
+
isLintable: true,
|
|
38183
|
+
getPublicWidgetOptions: perseusCore.getMatrixPublicWidgetOptions
|
|
39051
38184
|
};
|
|
39052
38185
|
|
|
39053
38186
|
const getPromptJSON$7 = () => {
|
|
@@ -42243,7 +41376,8 @@ var Plotter$1 = {
|
|
|
42243
41376
|
displayName: "Plotter",
|
|
42244
41377
|
hidden: true,
|
|
42245
41378
|
widget: Plotter,
|
|
42246
|
-
staticTransform: staticTransform
|
|
41379
|
+
staticTransform: staticTransform,
|
|
41380
|
+
getPublicWidgetOptions: perseusCore.getPlotterPublicWidgetOptions
|
|
42247
41381
|
};
|
|
42248
41382
|
|
|
42249
41383
|
const getPromptJSON$2 = () => {
|
|
@@ -42620,7 +41754,8 @@ var Table$1 = {
|
|
|
42620
41754
|
widget: Table,
|
|
42621
41755
|
transform: propTransform,
|
|
42622
41756
|
hidden: true,
|
|
42623
|
-
isLintable: true
|
|
41757
|
+
isLintable: true,
|
|
41758
|
+
getPublicWidgetOptions: perseusCore.getTablePublicWidgetOptions
|
|
42624
41759
|
};
|
|
42625
41760
|
|
|
42626
41761
|
const getPromptJSON = () => {
|
|
@@ -42801,7 +41936,7 @@ const init = function () {
|
|
|
42801
41936
|
|
|
42802
41937
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
42803
41938
|
const libName = "@khanacademy/perseus";
|
|
42804
|
-
const libVersion = "53.
|
|
41939
|
+
const libVersion = "53.1.0";
|
|
42805
41940
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
42806
41941
|
|
|
42807
41942
|
const apiVersion = {
|