@khanacademy/perseus 20.8.0 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.js +663 -378
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +674 -373
- package/dist/index.js.map +1 -1
- package/dist/util/extract-perseus-data.d.ts +6 -7
- package/dist/util/snowman-utils.d.ts +4 -77
- package/dist/util/test-utils.d.ts +37 -2
- package/dist/util/widget-enum-utils.d.ts +1 -3
- package/dist/widget-type-utils.d.ts +64 -0
- package/dist/widgets/interactive-graph.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/components/angle.d.ts +23 -0
- package/dist/widgets/interactive-graphs/graphs/components/css-transform-wrapper.d.ts +7 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/components/text-label.d.ts +6 -0
- package/dist/widgets/interactive-graphs/graphs/use-transform.d.ts +2 -0
- package/dist/widgets/interactive-graphs/graphs/utils.d.ts +9 -0
- package/dist/widgets/interactive-graphs/legacy-grid.d.ts +6 -1
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +3 -0
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-state.d.ts +1 -1
- package/package.json +2 -6
package/dist/index.js
CHANGED
|
@@ -12,9 +12,8 @@ var classNames = require('classnames');
|
|
|
12
12
|
var React = require('react');
|
|
13
13
|
var _ = require('underscore');
|
|
14
14
|
var Clickable = require('@khanacademy/wonder-blocks-clickable');
|
|
15
|
-
var Color = require('@khanacademy/wonder-blocks-color');
|
|
16
15
|
var wonderBlocksPopover = require('@khanacademy/wonder-blocks-popover');
|
|
17
|
-
var
|
|
16
|
+
var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
|
|
18
17
|
var aphrodite = require('aphrodite');
|
|
19
18
|
var $ = require('jquery');
|
|
20
19
|
var PropTypes = require('prop-types');
|
|
@@ -33,7 +32,6 @@ var caretDown = require('@phosphor-icons/core/assets/regular/caret-down.svg');
|
|
|
33
32
|
var caretUp = require('@phosphor-icons/core/assets/regular/caret-up.svg');
|
|
34
33
|
var debounce$1 = require('lodash.debounce');
|
|
35
34
|
var mafs = require('mafs');
|
|
36
|
-
var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
|
|
37
35
|
var wonderBlocksIcon = require('@khanacademy/wonder-blocks-icon');
|
|
38
36
|
var questionIcon = require('@phosphor-icons/core/regular/question.svg');
|
|
39
37
|
var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
|
|
@@ -73,8 +71,6 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
|
73
71
|
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
74
72
|
var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
|
|
75
73
|
var Clickable__default = /*#__PURE__*/_interopDefaultLegacy(Clickable);
|
|
76
|
-
var Color__default = /*#__PURE__*/_interopDefaultLegacy(Color);
|
|
77
|
-
var Spacing__default = /*#__PURE__*/_interopDefaultLegacy(Spacing);
|
|
78
74
|
var $__default = /*#__PURE__*/_interopDefaultLegacy($);
|
|
79
75
|
var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
|
|
80
76
|
var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
|
|
@@ -361,16 +357,16 @@ const MathInputIcon = _ref => {
|
|
|
361
357
|
focused,
|
|
362
358
|
active
|
|
363
359
|
} = _ref;
|
|
364
|
-
let
|
|
360
|
+
let fillColor;
|
|
365
361
|
switch (true) {
|
|
366
362
|
case focused || active:
|
|
367
|
-
|
|
363
|
+
fillColor = wonderBlocksTokens.color.white;
|
|
368
364
|
break;
|
|
369
365
|
case hovered:
|
|
370
|
-
|
|
366
|
+
fillColor = wonderBlocksTokens.color.blue;
|
|
371
367
|
break;
|
|
372
368
|
default:
|
|
373
|
-
|
|
369
|
+
fillColor = wonderBlocksTokens.color.offBlack;
|
|
374
370
|
break;
|
|
375
371
|
}
|
|
376
372
|
const dynamicClass = active || focused ? styles$D.iconActive : styles$D.iconInactive;
|
|
@@ -380,7 +376,7 @@ const MathInputIcon = _ref => {
|
|
|
380
376
|
xmlns: "http://www.w3.org/2000/svg",
|
|
381
377
|
width: "16",
|
|
382
378
|
height: "16",
|
|
383
|
-
fill:
|
|
379
|
+
fill: fillColor,
|
|
384
380
|
viewBox: "0 0 256 256"
|
|
385
381
|
}, /*#__PURE__*/React__namespace.createElement("path", {
|
|
386
382
|
d: "M112,72a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16h64A8,8,0,0,1,112,72Zm-8,104H80V152a8,8,0,0,0-16,0v24H40a8,8,0,0,0,0,16H64v24a8,8,0,0,0,16,0V192h24a8,8,0,0,0,0-16Zm48,0h64a8,8,0,0,0,0-16H152a8,8,0,0,0,0,16Zm64,16H152a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16Zm-61.66-90.34a8,8,0,0,0,11.32,0L184,83.31l18.34,18.35a8,8,0,0,0,11.32-11.32L195.31,72l18.35-18.34a8,8,0,0,0-11.32-11.32L184,60.69,165.66,42.34a8,8,0,0,0-11.32,11.32L172.69,72,154.34,90.34A8,8,0,0,0,154.34,101.66Z"
|
|
@@ -417,7 +413,7 @@ const mapButtonSets = buttonSets => {
|
|
|
417
413
|
};
|
|
418
414
|
const inputFocused = {
|
|
419
415
|
borderWidth: 2,
|
|
420
|
-
borderColor:
|
|
416
|
+
borderColor: wonderBlocksTokens.color.blue,
|
|
421
417
|
margin: -1
|
|
422
418
|
};
|
|
423
419
|
const styles$D = aphrodite.StyleSheet.create({
|
|
@@ -425,35 +421,35 @@ const styles$D = aphrodite.StyleSheet.create({
|
|
|
425
421
|
display: "flex",
|
|
426
422
|
justifyContent: "center",
|
|
427
423
|
height: "100%",
|
|
428
|
-
padding:
|
|
424
|
+
padding: wonderBlocksTokens.spacing.xxxSmall_4,
|
|
429
425
|
borderRadius: 1
|
|
430
426
|
},
|
|
431
427
|
iconInactive: {
|
|
432
|
-
backgroundColor:
|
|
428
|
+
backgroundColor: wonderBlocksTokens.color.offBlack8
|
|
433
429
|
},
|
|
434
430
|
iconActive: {
|
|
435
|
-
backgroundColor:
|
|
431
|
+
backgroundColor: wonderBlocksTokens.color.offBlack64
|
|
436
432
|
},
|
|
437
433
|
outerWrapper: {
|
|
438
434
|
display: "inline-block",
|
|
439
435
|
borderStyle: "solid",
|
|
440
436
|
borderWidth: 1,
|
|
441
|
-
borderColor:
|
|
437
|
+
borderColor: wonderBlocksTokens.color.offBlack50,
|
|
442
438
|
borderRadius: 3,
|
|
443
|
-
background:
|
|
439
|
+
background: wonderBlocksTokens.color.white,
|
|
444
440
|
":hover": inputFocused
|
|
445
441
|
},
|
|
446
442
|
wrapperFocused: inputFocused,
|
|
447
443
|
wrapperError: {
|
|
448
|
-
borderColor:
|
|
449
|
-
background:
|
|
444
|
+
borderColor: wonderBlocksTokens.color.red,
|
|
445
|
+
background: wonderBlocksTokens.color.fadedRed8,
|
|
450
446
|
":hover": {
|
|
451
|
-
borderColor:
|
|
447
|
+
borderColor: wonderBlocksTokens.color.red
|
|
452
448
|
}
|
|
453
449
|
},
|
|
454
450
|
popoverContent: {
|
|
455
451
|
padding: 0,
|
|
456
|
-
paddingBottom:
|
|
452
|
+
paddingBottom: wonderBlocksTokens.spacing.xxSmall_6,
|
|
457
453
|
maxWidth: "initial"
|
|
458
454
|
}
|
|
459
455
|
});
|
|
@@ -18894,7 +18890,7 @@ const isAccessible = widgetInfo => {
|
|
|
18894
18890
|
}
|
|
18895
18891
|
return !!accessible;
|
|
18896
18892
|
};
|
|
18897
|
-
const getAllWidgetTypes
|
|
18893
|
+
const getAllWidgetTypes = () => {
|
|
18898
18894
|
return ___default["default"].keys(widgets);
|
|
18899
18895
|
};
|
|
18900
18896
|
const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
|
|
@@ -19166,7 +19162,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
19166
19162
|
getVersionVector: getVersionVector,
|
|
19167
19163
|
getPublicWidgets: getPublicWidgets,
|
|
19168
19164
|
isAccessible: isAccessible,
|
|
19169
|
-
getAllWidgetTypes: getAllWidgetTypes
|
|
19165
|
+
getAllWidgetTypes: getAllWidgetTypes,
|
|
19170
19166
|
upgradeWidgetInfoToLatestVersion: upgradeWidgetInfoToLatestVersion,
|
|
19171
19167
|
getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
|
|
19172
19168
|
traverseChildWidgets: traverseChildWidgets$3,
|
|
@@ -19564,6 +19560,9 @@ class Renderer extends React__namespace.Component {
|
|
|
19564
19560
|
if (!widgetInfo.type || !widgetInfo.alignment) {
|
|
19565
19561
|
const newValues = {};
|
|
19566
19562
|
if (!widgetInfo.type) {
|
|
19563
|
+
// TODO: why does widget have no type?
|
|
19564
|
+
// We don't want to derive type from widget ID
|
|
19565
|
+
// see: LEMS-1845
|
|
19567
19566
|
newValues.type = widgetId.split(" ")[0];
|
|
19568
19567
|
}
|
|
19569
19568
|
if (!widgetInfo.alignment) {
|
|
@@ -21863,13 +21862,13 @@ const styles$A = aphrodite.StyleSheet.create({
|
|
|
21863
21862
|
width: 40
|
|
21864
21863
|
},
|
|
21865
21864
|
leftAlign: {
|
|
21866
|
-
paddingLeft:
|
|
21865
|
+
paddingLeft: wonderBlocksTokens.spacing.xxxSmall_4,
|
|
21867
21866
|
paddingRight: 0
|
|
21868
21867
|
},
|
|
21869
21868
|
rightAlign: {
|
|
21870
21869
|
textAlign: "right",
|
|
21871
21870
|
paddingLeft: 0,
|
|
21872
|
-
paddingRight:
|
|
21871
|
+
paddingRight: wonderBlocksTokens.spacing.xxxSmall_4
|
|
21873
21872
|
},
|
|
21874
21873
|
answerStateUnanswered: {
|
|
21875
21874
|
backgroundColor: "#eee",
|
|
@@ -22966,17 +22965,17 @@ function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, cor
|
|
|
22966
22965
|
let borderColor;
|
|
22967
22966
|
let color;
|
|
22968
22967
|
if (!showCorrectness && pressed) {
|
|
22969
|
-
borderColor =
|
|
22970
|
-
color =
|
|
22968
|
+
borderColor = wonderBlocksTokens.color.blue;
|
|
22969
|
+
color = wonderBlocksTokens.color.blue;
|
|
22971
22970
|
backgroundColor = "transparent";
|
|
22972
22971
|
} else if (checked) {
|
|
22973
|
-
const bg = showCorrectness && correct ?
|
|
22972
|
+
const bg = showCorrectness && correct ? wonderBlocksTokens.color.green : wonderBlocksTokens.color.blue;
|
|
22974
22973
|
color = white$1;
|
|
22975
22974
|
backgroundColor = bg;
|
|
22976
22975
|
borderColor = bg;
|
|
22977
22976
|
} else {
|
|
22978
|
-
borderColor =
|
|
22979
|
-
color =
|
|
22977
|
+
borderColor = wonderBlocksTokens.color.offBlack64;
|
|
22978
|
+
color = wonderBlocksTokens.color.offBlack64;
|
|
22980
22979
|
}
|
|
22981
22980
|
|
|
22982
22981
|
// define shape
|
|
@@ -23010,7 +23009,7 @@ const ChoiceIcon = function (props) {
|
|
|
23010
23009
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
23011
23010
|
className: aphrodite.css(styles$y.iconWrapper)
|
|
23012
23011
|
}, /*#__PURE__*/React__namespace.createElement(FocusRing, {
|
|
23013
|
-
color:
|
|
23012
|
+
color: wonderBlocksTokens.color.blue,
|
|
23014
23013
|
visible: focused || hovered,
|
|
23015
23014
|
multipleSelect: multipleSelect
|
|
23016
23015
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
@@ -23075,9 +23074,9 @@ const styles$w = aphrodite.StyleSheet.create({
|
|
|
23075
23074
|
color: gray68
|
|
23076
23075
|
},
|
|
23077
23076
|
circleIncorrectAnswered: {
|
|
23078
|
-
backgroundColor:
|
|
23079
|
-
borderColor:
|
|
23080
|
-
color:
|
|
23077
|
+
backgroundColor: wonderBlocksTokens.color.red,
|
|
23078
|
+
borderColor: wonderBlocksTokens.color.red,
|
|
23079
|
+
color: wonderBlocksTokens.color.white
|
|
23081
23080
|
}
|
|
23082
23081
|
});
|
|
23083
23082
|
|
|
@@ -23145,13 +23144,13 @@ const styles$v = aphrodite.StyleSheet.create({
|
|
|
23145
23144
|
textTransform: "uppercase"
|
|
23146
23145
|
},
|
|
23147
23146
|
correct: {
|
|
23148
|
-
color:
|
|
23147
|
+
color: wonderBlocksTokens.color.green
|
|
23149
23148
|
},
|
|
23150
23149
|
incorrectAnswered: {
|
|
23151
|
-
color:
|
|
23150
|
+
color: wonderBlocksTokens.color.red
|
|
23152
23151
|
},
|
|
23153
23152
|
incorrect: {
|
|
23154
|
-
color:
|
|
23153
|
+
color: wonderBlocksTokens.color.offBlack64
|
|
23155
23154
|
}
|
|
23156
23155
|
});
|
|
23157
23156
|
|
|
@@ -23225,7 +23224,7 @@ const Choice = function (props) {
|
|
|
23225
23224
|
// @ts-expect-error - TS2322 - Type '{ dispay: string; flexDirection: "column"; color: string; }' is not assignable to type 'Properties<string | number, string & {}>'.
|
|
23226
23225
|
dispay: "flex",
|
|
23227
23226
|
flexDirection: "column",
|
|
23228
|
-
color:
|
|
23227
|
+
color: wonderBlocksTokens.color.offBlack
|
|
23229
23228
|
},
|
|
23230
23229
|
className: descriptionClassName
|
|
23231
23230
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
@@ -23268,7 +23267,7 @@ const Choice = function (props) {
|
|
|
23268
23267
|
disabled: disabled || apiOptions.readOnly,
|
|
23269
23268
|
style: {
|
|
23270
23269
|
flex: 1,
|
|
23271
|
-
color:
|
|
23270
|
+
color: wonderBlocksTokens.color.offBlack,
|
|
23272
23271
|
userSelect: "text"
|
|
23273
23272
|
},
|
|
23274
23273
|
ref: forwardedRef,
|
|
@@ -23285,9 +23284,9 @@ const Choice = function (props) {
|
|
|
23285
23284
|
flexDirection: "row",
|
|
23286
23285
|
justifyContent: "center",
|
|
23287
23286
|
alignContent: "center",
|
|
23288
|
-
paddingTop:
|
|
23289
|
-
paddingBottom:
|
|
23290
|
-
paddingLeft:
|
|
23287
|
+
paddingTop: wonderBlocksTokens.spacing.xSmall_8,
|
|
23288
|
+
paddingBottom: wonderBlocksTokens.spacing.xSmall_8,
|
|
23289
|
+
paddingLeft: wonderBlocksTokens.spacing.xSmall_8
|
|
23291
23290
|
}
|
|
23292
23291
|
}, /*#__PURE__*/React__namespace.createElement(ChoiceIcon, {
|
|
23293
23292
|
pos: pos,
|
|
@@ -23303,7 +23302,7 @@ const Choice = function (props) {
|
|
|
23303
23302
|
previouslyAnswered: previouslyAnswered
|
|
23304
23303
|
}), /*#__PURE__*/React__namespace.createElement("span", {
|
|
23305
23304
|
style: {
|
|
23306
|
-
paddingLeft:
|
|
23305
|
+
paddingLeft: wonderBlocksTokens.spacing.small_12,
|
|
23307
23306
|
textAlign: "left",
|
|
23308
23307
|
flex: 1,
|
|
23309
23308
|
paddingTop: 4
|
|
@@ -23326,7 +23325,7 @@ const Choice = function (props) {
|
|
|
23326
23325
|
content: i18n__namespace._("Cross out option"),
|
|
23327
23326
|
closeButtonVisible: true,
|
|
23328
23327
|
actions: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksLayout.Strut, {
|
|
23329
|
-
size:
|
|
23328
|
+
size: wonderBlocksTokens.spacing.medium_16
|
|
23330
23329
|
}), /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
|
|
23331
23330
|
kind: "primary",
|
|
23332
23331
|
"aria-label": i18n__namespace._("Cross out Choice %(letter)s", {
|
|
@@ -23374,7 +23373,7 @@ const Choice = function (props) {
|
|
|
23374
23373
|
return /*#__PURE__*/React__namespace.createElement(Icon, {
|
|
23375
23374
|
icon: ellipsisHorizontalIcon,
|
|
23376
23375
|
size: 3,
|
|
23377
|
-
color:
|
|
23376
|
+
color: wonderBlocksTokens.color.offBlack64
|
|
23378
23377
|
});
|
|
23379
23378
|
});
|
|
23380
23379
|
})), showRationale && /*#__PURE__*/React__namespace.createElement("div", {
|
|
@@ -23392,7 +23391,7 @@ const styles$u = aphrodite.StyleSheet.create({
|
|
|
23392
23391
|
padding: intermediateCheckboxPadding,
|
|
23393
23392
|
paddingTop: 0,
|
|
23394
23393
|
marginLeft: 54,
|
|
23395
|
-
color:
|
|
23394
|
+
color: wonderBlocksTokens.color.offBlack64,
|
|
23396
23395
|
[mediaQueries.smOrSmaller]: {
|
|
23397
23396
|
padding: intermediateCheckboxPaddingPhone,
|
|
23398
23397
|
paddingTop: 0
|
|
@@ -24818,8 +24817,8 @@ class Definition extends React__namespace.Component {
|
|
|
24818
24817
|
} = _ref2;
|
|
24819
24818
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
24820
24819
|
style: {
|
|
24821
|
-
color:
|
|
24822
|
-
borderBottom: hovered || focused || pressed ? `2px solid ${
|
|
24820
|
+
color: wonderBlocksTokens.color.blue,
|
|
24821
|
+
borderBottom: hovered || focused || pressed ? `2px solid ${wonderBlocksTokens.color.blue}` : "none"
|
|
24823
24822
|
}
|
|
24824
24823
|
}, this.props.togglePrompt);
|
|
24825
24824
|
}));
|
|
@@ -24828,7 +24827,7 @@ class Definition extends React__namespace.Component {
|
|
|
24828
24827
|
}
|
|
24829
24828
|
const styles$q = {
|
|
24830
24829
|
tooltipBody: {
|
|
24831
|
-
color:
|
|
24830
|
+
color: wonderBlocksTokens.color.offBlack,
|
|
24832
24831
|
fontSize: 20,
|
|
24833
24832
|
fontWeight: 500,
|
|
24834
24833
|
lineHeight: "30px"
|
|
@@ -25126,7 +25125,7 @@ class GradedGroupAnswerBar extends React__namespace.Component {
|
|
|
25126
25125
|
} = this.props;
|
|
25127
25126
|
const answerBarStyle = {
|
|
25128
25127
|
...styles$o.answerBar,
|
|
25129
|
-
backgroundColor: answerBarState === "CORRECT" ?
|
|
25128
|
+
backgroundColor: answerBarState === "CORRECT" ? wonderBlocksTokens.color.offWhite : "white",
|
|
25130
25129
|
// Center the "Correct!" message only when there's no next question
|
|
25131
25130
|
justifyContent: answerBarState === "CORRECT" && !onNextQuestion ? "center" : "space-between"
|
|
25132
25131
|
};
|
|
@@ -25156,7 +25155,7 @@ class GradedGroupAnswerBar extends React__namespace.Component {
|
|
|
25156
25155
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
25157
25156
|
style: {
|
|
25158
25157
|
fontSize: 28,
|
|
25159
|
-
color:
|
|
25158
|
+
color: wonderBlocksTokens.color.green
|
|
25160
25159
|
}
|
|
25161
25160
|
}, /*#__PURE__*/React__namespace.createElement(InlineIcon, _extends({}, iconStar, {
|
|
25162
25161
|
style: {
|
|
@@ -25186,7 +25185,7 @@ const styles$o = {
|
|
|
25186
25185
|
marginTop: phoneMargin,
|
|
25187
25186
|
paddingLeft: phoneMargin,
|
|
25188
25187
|
paddingRight: 10,
|
|
25189
|
-
borderTop: `1px solid ${
|
|
25188
|
+
borderTop: `1px solid ${wonderBlocksTokens.color.offBlack50}`
|
|
25190
25189
|
},
|
|
25191
25190
|
tryAgainIcon: {
|
|
25192
25191
|
fontSize: 28,
|
|
@@ -25494,7 +25493,7 @@ const styles$n = aphrodite.StyleSheet.create({
|
|
|
25494
25493
|
padding: 0,
|
|
25495
25494
|
border: "none",
|
|
25496
25495
|
marginTop: 20,
|
|
25497
|
-
color:
|
|
25496
|
+
color: wonderBlocksTokens.color.blue,
|
|
25498
25497
|
cursor: "pointer",
|
|
25499
25498
|
display: "block",
|
|
25500
25499
|
clear: "both"
|
|
@@ -25502,7 +25501,7 @@ const styles$n = aphrodite.StyleSheet.create({
|
|
|
25502
25501
|
explanationTitle: {
|
|
25503
25502
|
backgroundColor: "unset",
|
|
25504
25503
|
marginTop: 20,
|
|
25505
|
-
color:
|
|
25504
|
+
color: wonderBlocksTokens.color.blue,
|
|
25506
25505
|
marginBottom: 10,
|
|
25507
25506
|
cursor: "pointer",
|
|
25508
25507
|
fontSize: 14,
|
|
@@ -25718,7 +25717,7 @@ const styles$m = aphrodite.StyleSheet.create({
|
|
|
25718
25717
|
},
|
|
25719
25718
|
title: {
|
|
25720
25719
|
fontSize: 12,
|
|
25721
|
-
color:
|
|
25720
|
+
color: wonderBlocksTokens.color.offBlack64,
|
|
25722
25721
|
textTransform: "uppercase",
|
|
25723
25722
|
marginBottom: 11,
|
|
25724
25723
|
letterSpacing: 0.8
|
|
@@ -25752,7 +25751,7 @@ const styles$m = aphrodite.StyleSheet.create({
|
|
|
25752
25751
|
height: 10,
|
|
25753
25752
|
borderRadius: "100%",
|
|
25754
25753
|
borderWidth: 2,
|
|
25755
|
-
borderColor:
|
|
25754
|
+
borderColor: wonderBlocksTokens.color.blue,
|
|
25756
25755
|
borderStyle: "solid"
|
|
25757
25756
|
},
|
|
25758
25757
|
indicatorDotFocused: {
|
|
@@ -25760,7 +25759,7 @@ const styles$m = aphrodite.StyleSheet.create({
|
|
|
25760
25759
|
borderStyle: "double"
|
|
25761
25760
|
},
|
|
25762
25761
|
indicatorDotActive: {
|
|
25763
|
-
backgroundColor:
|
|
25762
|
+
backgroundColor: wonderBlocksTokens.color.blue,
|
|
25764
25763
|
width: "100%",
|
|
25765
25764
|
height: "100%"
|
|
25766
25765
|
},
|
|
@@ -28370,6 +28369,61 @@ const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.
|
|
|
28370
28369
|
return (coord + ranges[i][1]) / extent;
|
|
28371
28370
|
}));
|
|
28372
28371
|
|
|
28372
|
+
const useTransform = function () {
|
|
28373
|
+
const {
|
|
28374
|
+
viewTransform,
|
|
28375
|
+
userTransform
|
|
28376
|
+
} = mafs.useTransformContext();
|
|
28377
|
+
const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
|
|
28378
|
+
for (var _len = arguments.length, points = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
28379
|
+
points[_key] = arguments[_key];
|
|
28380
|
+
}
|
|
28381
|
+
const transformedPoints = points.map(point => mafs.vec.transform(point, transformToPx));
|
|
28382
|
+
return transformedPoints;
|
|
28383
|
+
};
|
|
28384
|
+
|
|
28385
|
+
/**
|
|
28386
|
+
* Given two points, find the tips that extends through the points to the edge of the range.
|
|
28387
|
+
* @param collinearPoint - The point that the line passes through. Needed to establish slope.
|
|
28388
|
+
* @param extendFrom - The point that the line extends from to the edge of the graph.
|
|
28389
|
+
*/
|
|
28390
|
+
const getRayIntersectionCoords = (collinearPoint, extendFrom, range) => {
|
|
28391
|
+
// edges of the graph
|
|
28392
|
+
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
28393
|
+
const [aX, aY] = collinearPoint;
|
|
28394
|
+
const [bX, bY] = extendFrom;
|
|
28395
|
+
const yDiff = bY - aY;
|
|
28396
|
+
const xDiff = bX - aX;
|
|
28397
|
+
const slope = yDiff / xDiff;
|
|
28398
|
+
const yAtXMin = slope * (xMin - aX) + aY;
|
|
28399
|
+
const yAtXMax = slope * (xMax - aX) + aY;
|
|
28400
|
+
const xAtYMin = (yMin - aY) / slope + aX;
|
|
28401
|
+
const xAtYMax = (yMax - aY) / slope + aX;
|
|
28402
|
+
|
|
28403
|
+
// clock analogy to describe quadrants
|
|
28404
|
+
switch (true) {
|
|
28405
|
+
// 12 o'clock to 2:59
|
|
28406
|
+
case yDiff > 0 && xDiff >= 0:
|
|
28407
|
+
return xAtYMax > xMax ? [xMax, yAtXMax] : [xAtYMax, yMax];
|
|
28408
|
+
// 3 o'clock to 5:59
|
|
28409
|
+
case yDiff <= 0 && xDiff > 0:
|
|
28410
|
+
// xAtYMin evaluates to -Infinity here, so we use absolute value
|
|
28411
|
+
return Math.abs(xAtYMin) > xMax ? [xMax, yAtXMax] : [xAtYMin, yMin];
|
|
28412
|
+
// 9 o'clock to 11:59
|
|
28413
|
+
case yDiff >= 0 && xDiff < 0:
|
|
28414
|
+
return xAtYMax < xMin ? [xMin, yAtXMin] : [xAtYMax, yMax];
|
|
28415
|
+
// 6 o'clock to 8:59
|
|
28416
|
+
case yDiff < 0 && xDiff <= 0:
|
|
28417
|
+
return xAtYMin < xMin ? [xMin, yAtXMin] : [xAtYMin, yMin];
|
|
28418
|
+
default:
|
|
28419
|
+
return [xMax, yAtXMax];
|
|
28420
|
+
}
|
|
28421
|
+
};
|
|
28422
|
+
const getLines = points => points.map((point, i) => {
|
|
28423
|
+
const next = points[(i + 1) % points.length];
|
|
28424
|
+
return [point, next];
|
|
28425
|
+
});
|
|
28426
|
+
|
|
28373
28427
|
const defaultStroke = "var(--movable-line-stroke-color)";
|
|
28374
28428
|
const MovableLine = props => {
|
|
28375
28429
|
const {
|
|
@@ -28380,18 +28434,12 @@ const MovableLine = props => {
|
|
|
28380
28434
|
stroke = defaultStroke
|
|
28381
28435
|
} = props;
|
|
28382
28436
|
const midpoint = mafs.vec.midpoint(start, end);
|
|
28383
|
-
const
|
|
28384
|
-
viewTransform,
|
|
28385
|
-
userTransform
|
|
28386
|
-
} = mafs.useTransformContext();
|
|
28387
|
-
const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
|
|
28388
|
-
const startPtPx = mafs.vec.transform(start, transformToPx);
|
|
28389
|
-
const endPtPx = mafs.vec.transform(end, transformToPx);
|
|
28437
|
+
const [startPtPx, endPtPx] = useTransform(start, end);
|
|
28390
28438
|
let startExtend = undefined;
|
|
28391
28439
|
let endExtend = undefined;
|
|
28392
28440
|
if (extend) {
|
|
28393
|
-
startExtend = extend.start ?
|
|
28394
|
-
endExtend = extend.end ?
|
|
28441
|
+
startExtend = extend.start ? getRayIntersectionCoords(end, start, extend.range) : undefined;
|
|
28442
|
+
endExtend = extend.end ? getRayIntersectionCoords(start, end, extend.range) : undefined;
|
|
28395
28443
|
}
|
|
28396
28444
|
const line = React.useRef(null);
|
|
28397
28445
|
const {
|
|
@@ -28453,50 +28501,12 @@ function SVGLine(props) {
|
|
|
28453
28501
|
});
|
|
28454
28502
|
}
|
|
28455
28503
|
|
|
28456
|
-
/**
|
|
28457
|
-
* Given two points, find the tips that extends through the points to the edge of the range.
|
|
28458
|
-
* @param collinearPoint - The point that the line passes through. Needed to establish slope.
|
|
28459
|
-
* @param extendFrom - The point that the line extends from to the edge of the graph.
|
|
28460
|
-
*/
|
|
28461
|
-
const getExtensionCoords = (collinearPoint, extendFrom, range) => {
|
|
28462
|
-
// edges of the graph
|
|
28463
|
-
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
28464
|
-
const [aX, aY] = collinearPoint;
|
|
28465
|
-
const [bX, bY] = extendFrom;
|
|
28466
|
-
const yDiff = bY - aY;
|
|
28467
|
-
const xDiff = bX - aX;
|
|
28468
|
-
const slope = yDiff / xDiff;
|
|
28469
|
-
const yAtXMin = slope * (xMin - aX) + aY;
|
|
28470
|
-
const yAtXMax = slope * (xMax - aX) + aY;
|
|
28471
|
-
const xAtYMin = (yMin - aY) / slope + aX;
|
|
28472
|
-
const xAtYMax = (yMax - aY) / slope + aX;
|
|
28473
|
-
|
|
28474
|
-
// clock analogy to describe quadrants
|
|
28475
|
-
switch (true) {
|
|
28476
|
-
// 12 o'clock to 2:59
|
|
28477
|
-
case yDiff > 0 && xDiff >= 0:
|
|
28478
|
-
return xAtYMax > xMax ? [xMax, yAtXMax] : [xAtYMax, yMax];
|
|
28479
|
-
// 3 o'clock to 5:59
|
|
28480
|
-
case yDiff <= 0 && xDiff > 0:
|
|
28481
|
-
// xAtYMin evaluates to -Infinity here, so we use absolute value
|
|
28482
|
-
return Math.abs(xAtYMin) > xMax ? [xMax, yAtXMax] : [xAtYMin, yMin];
|
|
28483
|
-
// 9 o'clock to 11:59
|
|
28484
|
-
case yDiff >= 0 && xDiff < 0:
|
|
28485
|
-
return xAtYMax < xMin ? [xMin, yAtXMin] : [xAtYMax, yMax];
|
|
28486
|
-
// 6 o'clock to 8:59
|
|
28487
|
-
case yDiff < 0 && xDiff <= 0:
|
|
28488
|
-
return xAtYMin < xMin ? [xMin, yAtXMin] : [xAtYMin, yMin];
|
|
28489
|
-
default:
|
|
28490
|
-
return [xMax, yAtXMax];
|
|
28491
|
-
}
|
|
28492
|
-
};
|
|
28493
|
-
|
|
28494
28504
|
const StyledMovablePoint = props => {
|
|
28495
28505
|
const hitboxRef = React.useRef(null);
|
|
28496
28506
|
const {
|
|
28497
28507
|
point,
|
|
28498
28508
|
onMove,
|
|
28499
|
-
color =
|
|
28509
|
+
color = wonderBlocksTokens.color.blue
|
|
28500
28510
|
} = props;
|
|
28501
28511
|
const {
|
|
28502
28512
|
dragging
|
|
@@ -28506,12 +28516,7 @@ const StyledMovablePoint = props => {
|
|
|
28506
28516
|
onMove,
|
|
28507
28517
|
constrain: p => p
|
|
28508
28518
|
});
|
|
28509
|
-
const
|
|
28510
|
-
viewTransform,
|
|
28511
|
-
userTransform
|
|
28512
|
-
} = mafs.useTransformContext();
|
|
28513
|
-
const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
|
|
28514
|
-
const [x, y] = mafs.vec.transform(point, transformToPx);
|
|
28519
|
+
const [[x, y]] = useTransform(point);
|
|
28515
28520
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
28516
28521
|
ref: hitboxRef,
|
|
28517
28522
|
className: "movable-point",
|
|
@@ -28678,6 +28683,175 @@ const RayGraph = props => {
|
|
|
28678
28683
|
}));
|
|
28679
28684
|
};
|
|
28680
28685
|
|
|
28686
|
+
/**
|
|
28687
|
+
* Wrapper with Mafs transform styles for SVG elements.
|
|
28688
|
+
* Scales and translates the SVG element to match the Mafs view.
|
|
28689
|
+
* Use if you do not need/want to use the hook in `use-transform.ts`.
|
|
28690
|
+
*/
|
|
28691
|
+
const MafsCssTransformWrapper = _ref => {
|
|
28692
|
+
let {
|
|
28693
|
+
children
|
|
28694
|
+
} = _ref;
|
|
28695
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
28696
|
+
style: {
|
|
28697
|
+
transform: `var(--mafs-view-transform) var(--mafs-user-transform)`
|
|
28698
|
+
}
|
|
28699
|
+
}, children);
|
|
28700
|
+
};
|
|
28701
|
+
|
|
28702
|
+
const TextLabel = _ref => {
|
|
28703
|
+
let {
|
|
28704
|
+
children,
|
|
28705
|
+
...rest
|
|
28706
|
+
} = _ref;
|
|
28707
|
+
return /*#__PURE__*/React__default["default"].createElement(mafs.Text, _extends({
|
|
28708
|
+
size: 16,
|
|
28709
|
+
svgTextProps: {
|
|
28710
|
+
filter: "url(#background)",
|
|
28711
|
+
fontWeight: "bold"
|
|
28712
|
+
}
|
|
28713
|
+
}, rest), children);
|
|
28714
|
+
};
|
|
28715
|
+
const SvgDefs = () => /*#__PURE__*/React__default["default"].createElement("defs", null, /*#__PURE__*/React__default["default"].createElement("filter", {
|
|
28716
|
+
id: "background",
|
|
28717
|
+
x: "-5%",
|
|
28718
|
+
width: "110%",
|
|
28719
|
+
y: "0%",
|
|
28720
|
+
height: "100%"
|
|
28721
|
+
}, /*#__PURE__*/React__default["default"].createElement("feFlood", {
|
|
28722
|
+
floodColor: "#FFF",
|
|
28723
|
+
floodOpacity: "0.64"
|
|
28724
|
+
}), /*#__PURE__*/React__default["default"].createElement("feComposite", {
|
|
28725
|
+
operator: "over",
|
|
28726
|
+
in: "SourceGraphic"
|
|
28727
|
+
})));
|
|
28728
|
+
|
|
28729
|
+
const Angle = _ref => {
|
|
28730
|
+
let {
|
|
28731
|
+
centerPoint,
|
|
28732
|
+
endPoints,
|
|
28733
|
+
range,
|
|
28734
|
+
polygonLines,
|
|
28735
|
+
showAngles
|
|
28736
|
+
} = _ref;
|
|
28737
|
+
const [centerX, centerY] = centerPoint;
|
|
28738
|
+
const areClockwise = clockwise$1([centerPoint, ...endPoints]);
|
|
28739
|
+
const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
|
|
28740
|
+
const radius = 0.3;
|
|
28741
|
+
const a = mafs.vec.dist(centerPoint, endPoints[0]);
|
|
28742
|
+
const b = mafs.vec.dist(centerPoint, endPoints[1]);
|
|
28743
|
+
const c = mafs.vec.dist(endPoints[0], endPoints[1]);
|
|
28744
|
+
|
|
28745
|
+
// Law of cosines
|
|
28746
|
+
const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
|
|
28747
|
+
const y1 = centerY + (startY - centerY) / a * radius;
|
|
28748
|
+
const x2 = centerX + (endX - centerX) / b * radius;
|
|
28749
|
+
const x1 = centerX + (startX - centerX) / a * radius;
|
|
28750
|
+
const y2 = centerY + (endY - centerY) / b * radius;
|
|
28751
|
+
|
|
28752
|
+
// Fourth point that would create a rhomboid
|
|
28753
|
+
// Used to calculate the inside of the angle and to create square
|
|
28754
|
+
const [x3, y3] = mafs.vec.add(centerPoint, mafs.vec.add(mafs.vec.sub([x1, y1], centerPoint), mafs.vec.sub([x2, y2], centerPoint)));
|
|
28755
|
+
if (!showAngles) {
|
|
28756
|
+
return isRightAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
|
|
28757
|
+
start: [x1, y1],
|
|
28758
|
+
vertex: [x2, y2],
|
|
28759
|
+
end: [x3, y3]
|
|
28760
|
+
}) : null;
|
|
28761
|
+
}
|
|
28762
|
+
|
|
28763
|
+
// Midpoint betwen ends of arc
|
|
28764
|
+
const isOutside = shouldDrawArcOutside([x3, y3], centerPoint, range, polygonLines);
|
|
28765
|
+
const largeArcFlag = isOutside ? 1 : 0;
|
|
28766
|
+
const sweepFlag = isOutside ? 1 : 0;
|
|
28767
|
+
const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
|
|
28768
|
+
let angleInDegrees = angle * (180 / Math.PI);
|
|
28769
|
+
// If we have triggered "largArcFlag", the angle should be greater than 180
|
|
28770
|
+
if (isOutside) {
|
|
28771
|
+
angleInDegrees = 360 - angleInDegrees;
|
|
28772
|
+
}
|
|
28773
|
+
const angleLabelNumber = parseFloat(angleInDegrees.toFixed(1));
|
|
28774
|
+
const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
|
|
28775
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
|
|
28776
|
+
id: "background",
|
|
28777
|
+
x: "-5%",
|
|
28778
|
+
width: "110%",
|
|
28779
|
+
y: "0%",
|
|
28780
|
+
height: "100%"
|
|
28781
|
+
}, /*#__PURE__*/React__namespace.createElement("feFlood", {
|
|
28782
|
+
floodColor: "#FFF",
|
|
28783
|
+
floodOpacity: "0.5"
|
|
28784
|
+
}), /*#__PURE__*/React__namespace.createElement("feComposite", {
|
|
28785
|
+
operator: "over",
|
|
28786
|
+
in: "SourceGraphic"
|
|
28787
|
+
}))), !isOutside && isRightAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
|
|
28788
|
+
start: [x1, y1],
|
|
28789
|
+
vertex: [x2, y2],
|
|
28790
|
+
end: [x3, y3]
|
|
28791
|
+
}) : /*#__PURE__*/React__namespace.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React__namespace.createElement("path", {
|
|
28792
|
+
d: arc,
|
|
28793
|
+
strokeWidth: 0.02,
|
|
28794
|
+
fill: "none"
|
|
28795
|
+
})), /*#__PURE__*/React__namespace.createElement(TextLabel, {
|
|
28796
|
+
x: x3,
|
|
28797
|
+
y: y3,
|
|
28798
|
+
attach: y3 - centerY > 0 ? "s" : "n",
|
|
28799
|
+
attachDistance: Math.abs(y3 - centerY) < 0.2 || mafs.vec.dist([x3, y3], centerPoint) < 0.3 ? 20 : 10
|
|
28800
|
+
}, angleLabel, "\xB0"));
|
|
28801
|
+
};
|
|
28802
|
+
|
|
28803
|
+
/**
|
|
28804
|
+
* This is broken out into its own component so that it can be used for an early return
|
|
28805
|
+
* (see line 55 or https://github.com/Khan/perseus/blob/84bbd882b11d16871e1b813a0b901f3b903d5479/packages/perseus/src/widgets/interactive-graphs/graphs/components/angle.tsx#L53-L57)
|
|
28806
|
+
*/
|
|
28807
|
+
const RightAngleSquare = _ref2 => {
|
|
28808
|
+
let {
|
|
28809
|
+
start: [x1, y1],
|
|
28810
|
+
vertex: [x2, y2],
|
|
28811
|
+
end: [x3, y3]
|
|
28812
|
+
} = _ref2;
|
|
28813
|
+
return /*#__PURE__*/React__namespace.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React__namespace.createElement("path", {
|
|
28814
|
+
d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
|
|
28815
|
+
strokeWidth: 0.02,
|
|
28816
|
+
fill: "none"
|
|
28817
|
+
}));
|
|
28818
|
+
};
|
|
28819
|
+
const isRightAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
|
|
28820
|
+
|
|
28821
|
+
/**
|
|
28822
|
+
* Determines if an angle is an inside (false) or outside (true) angle.
|
|
28823
|
+
* They way, we know to flip the `largeArc` and `sweepArc` flags.
|
|
28824
|
+
* Uses the priciple that a ray from a point inside a polygon will intersect
|
|
28825
|
+
* with an odd number of lines, while a ray from a point outside the polygon
|
|
28826
|
+
* will intersect with an even number of lines.
|
|
28827
|
+
* https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon
|
|
28828
|
+
*/
|
|
28829
|
+
const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
|
|
28830
|
+
// Create a ray from the midpoint (inside angle) to the edge of the range
|
|
28831
|
+
const rangeIntersectionPoint = getRayIntersectionCoords(midpoint, vertex, range);
|
|
28832
|
+
let lineIntersectionCount = 0;
|
|
28833
|
+
polygonLines.forEach(line => linesIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
|
|
28834
|
+
|
|
28835
|
+
// If the number of intersections is even, the angle is inside the polygon
|
|
28836
|
+
return !isEven(lineIntersectionCount);
|
|
28837
|
+
};
|
|
28838
|
+
|
|
28839
|
+
// https://stackoverflow.com/a/24392281/7347484
|
|
28840
|
+
// The "intersects" function in geometry doesn't seem to work for this use case
|
|
28841
|
+
const linesIntersect = (_ref3, _ref4) => {
|
|
28842
|
+
let [[a, b], [c, d]] = _ref3;
|
|
28843
|
+
let [[p, q], [r, s]] = _ref4;
|
|
28844
|
+
const determinant = (c - a) * (s - q) - (r - p) * (d - b);
|
|
28845
|
+
if (determinant === 0) {
|
|
28846
|
+
return false;
|
|
28847
|
+
} else {
|
|
28848
|
+
const lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / determinant;
|
|
28849
|
+
const gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / determinant;
|
|
28850
|
+
return 0 < lambda && lambda < 1 && 0 < gamma && gamma < 1;
|
|
28851
|
+
}
|
|
28852
|
+
};
|
|
28853
|
+
const isEven = n => n % 2 === 0;
|
|
28854
|
+
|
|
28681
28855
|
const PolygonGraph = props => {
|
|
28682
28856
|
const [focused, setFocused] = React__namespace.useState(false);
|
|
28683
28857
|
const [hovered, setHovered] = React__namespace.useState(false);
|
|
@@ -28686,7 +28860,10 @@ const PolygonGraph = props => {
|
|
|
28686
28860
|
} = props;
|
|
28687
28861
|
const {
|
|
28688
28862
|
coords,
|
|
28689
|
-
type
|
|
28863
|
+
type,
|
|
28864
|
+
showAngles,
|
|
28865
|
+
showSides,
|
|
28866
|
+
range
|
|
28690
28867
|
} = props.graphState;
|
|
28691
28868
|
const points = coords ?? [[0, 0]];
|
|
28692
28869
|
const pointsSum = points.reduce((acc, point) => mafs.vec.add(acc, point), [0, 0]);
|
|
@@ -28704,12 +28881,37 @@ const PolygonGraph = props => {
|
|
|
28704
28881
|
constrain: p => p
|
|
28705
28882
|
});
|
|
28706
28883
|
const active = hovered || focused || dragging;
|
|
28884
|
+
const lines = getLines(points);
|
|
28707
28885
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
28708
28886
|
points: [...points],
|
|
28709
28887
|
color: "var(--movable-line-stroke-color)",
|
|
28710
28888
|
svgPolygonProps: {
|
|
28711
28889
|
strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)"
|
|
28712
28890
|
}
|
|
28891
|
+
}), points.map((point, i) => {
|
|
28892
|
+
const pt1 = points.at(i - 1);
|
|
28893
|
+
const pt2 = points[(i + 1) % points.length];
|
|
28894
|
+
if (!pt1 || !pt2) {
|
|
28895
|
+
return null;
|
|
28896
|
+
}
|
|
28897
|
+
return /*#__PURE__*/React__namespace.createElement(Angle, {
|
|
28898
|
+
key: "angle-" + i,
|
|
28899
|
+
centerPoint: point,
|
|
28900
|
+
endPoints: [pt1, pt2],
|
|
28901
|
+
active: active,
|
|
28902
|
+
range: range,
|
|
28903
|
+
polygonLines: lines,
|
|
28904
|
+
showAngles: !!showAngles
|
|
28905
|
+
});
|
|
28906
|
+
}), showSides && lines.map((_ref, i) => {
|
|
28907
|
+
let [start, end] = _ref;
|
|
28908
|
+
const [x, y] = mafs.vec.midpoint(start, end);
|
|
28909
|
+
const length = parseFloat(mafs.vec.dist(start, end).toFixed(1));
|
|
28910
|
+
return /*#__PURE__*/React__namespace.createElement(TextLabel, {
|
|
28911
|
+
key: "side-" + i,
|
|
28912
|
+
x: x,
|
|
28913
|
+
y: y
|
|
28914
|
+
}, !Number.isInteger(length) && "≈ ", length);
|
|
28713
28915
|
}), /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
28714
28916
|
points: [...points],
|
|
28715
28917
|
color: "transparent",
|
|
@@ -28727,7 +28929,7 @@ const PolygonGraph = props => {
|
|
|
28727
28929
|
className: "movable-polygon"
|
|
28728
28930
|
}
|
|
28729
28931
|
}), points.map((point, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
28730
|
-
key: i,
|
|
28932
|
+
key: "point-" + i,
|
|
28731
28933
|
point: point,
|
|
28732
28934
|
onMove: destination => dispatch(moveControlPoint(i, destination)),
|
|
28733
28935
|
"data-testid": type + i
|
|
@@ -29409,7 +29611,11 @@ var components = /*#__PURE__*/Object.freeze({
|
|
|
29409
29611
|
* If a graphie URL is provided in `backgroundImage`, will return the rendered graphie background.
|
|
29410
29612
|
* Otherwise, returns `null`.
|
|
29411
29613
|
*/
|
|
29412
|
-
const
|
|
29614
|
+
const LegacyGrid = _ref => {
|
|
29615
|
+
let {
|
|
29616
|
+
box,
|
|
29617
|
+
backgroundImage
|
|
29618
|
+
} = _ref;
|
|
29413
29619
|
const {
|
|
29414
29620
|
url,
|
|
29415
29621
|
width,
|
|
@@ -29423,10 +29629,10 @@ const getLegacyGrid = (box, backgroundImage) => {
|
|
|
29423
29629
|
bottom: 0,
|
|
29424
29630
|
left: 0
|
|
29425
29631
|
}
|
|
29426
|
-
}, /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null,
|
|
29632
|
+
}, /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null, _ref2 => {
|
|
29427
29633
|
let {
|
|
29428
29634
|
setAssetStatus
|
|
29429
|
-
} =
|
|
29635
|
+
} = _ref2;
|
|
29430
29636
|
return /*#__PURE__*/React__namespace.createElement(SvgImage, {
|
|
29431
29637
|
src: url,
|
|
29432
29638
|
width: width,
|
|
@@ -29698,10 +29904,10 @@ function initializeGraphState(params) {
|
|
|
29698
29904
|
};
|
|
29699
29905
|
case "polygon":
|
|
29700
29906
|
return {
|
|
29701
|
-
type: "polygon",
|
|
29702
29907
|
hasBeenInteractedWith: false,
|
|
29703
29908
|
range,
|
|
29704
29909
|
snapStep,
|
|
29910
|
+
...graph,
|
|
29705
29911
|
coords: getPolygonCoords({
|
|
29706
29912
|
graph,
|
|
29707
29913
|
range,
|
|
@@ -29723,31 +29929,41 @@ function initializeGraphState(params) {
|
|
|
29723
29929
|
// Instead of updating to v5, using generic type to relate input and output types.
|
|
29724
29930
|
function getGradableGraph(state, initialGraph) {
|
|
29725
29931
|
if (!state.hasBeenInteractedWith) {
|
|
29726
|
-
return
|
|
29932
|
+
return {
|
|
29933
|
+
type: initialGraph.type
|
|
29934
|
+
};
|
|
29727
29935
|
}
|
|
29728
|
-
|
|
29729
|
-
|
|
29730
|
-
|
|
29731
|
-
|
|
29732
|
-
|
|
29733
|
-
...initialGraph,
|
|
29734
|
-
coords: state.coords
|
|
29735
|
-
};
|
|
29736
|
-
// coords: CollinearTuple
|
|
29737
|
-
case state.type === "linear" && initialGraph.type === "linear":
|
|
29738
|
-
case state.type === "ray" && initialGraph.type === "ray":
|
|
29739
|
-
return {
|
|
29740
|
-
...initialGraph,
|
|
29741
|
-
coords: state.coords?.[0]
|
|
29742
|
-
};
|
|
29743
|
-
case state.type === "polygon" && initialGraph.type === "polygon":
|
|
29744
|
-
return {
|
|
29745
|
-
...initialGraph,
|
|
29746
|
-
coords: state.coords
|
|
29747
|
-
};
|
|
29748
|
-
default:
|
|
29749
|
-
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29936
|
+
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
29937
|
+
return {
|
|
29938
|
+
...initialGraph,
|
|
29939
|
+
coords: state.coords
|
|
29940
|
+
};
|
|
29750
29941
|
}
|
|
29942
|
+
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
29943
|
+
return {
|
|
29944
|
+
...initialGraph,
|
|
29945
|
+
coords: state.coords
|
|
29946
|
+
};
|
|
29947
|
+
}
|
|
29948
|
+
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
29949
|
+
return {
|
|
29950
|
+
...initialGraph,
|
|
29951
|
+
coords: state.coords?.[0]
|
|
29952
|
+
};
|
|
29953
|
+
}
|
|
29954
|
+
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
29955
|
+
return {
|
|
29956
|
+
...initialGraph,
|
|
29957
|
+
coords: state.coords?.[0]
|
|
29958
|
+
};
|
|
29959
|
+
}
|
|
29960
|
+
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
29961
|
+
return {
|
|
29962
|
+
...initialGraph,
|
|
29963
|
+
coords: state.coords
|
|
29964
|
+
};
|
|
29965
|
+
}
|
|
29966
|
+
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29751
29967
|
}
|
|
29752
29968
|
const getDefaultSegments = _ref => {
|
|
29753
29969
|
let {
|
|
@@ -29771,9 +29987,10 @@ const getDefaultSegments = _ref => {
|
|
|
29771
29987
|
return [5];
|
|
29772
29988
|
}
|
|
29773
29989
|
};
|
|
29990
|
+
const defaultRange = [[-10, 10], [-10, 10]];
|
|
29774
29991
|
return ys(graph.numSegments).map(y => {
|
|
29775
29992
|
let endpoints = [[-5, y], [5, y]];
|
|
29776
|
-
endpoints = normalizeCoords(endpoints,
|
|
29993
|
+
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
29777
29994
|
endpoints = normalizePoints(range, step, endpoints);
|
|
29778
29995
|
return endpoints;
|
|
29779
29996
|
});
|
|
@@ -29856,7 +30073,6 @@ const renderGraph = props => {
|
|
|
29856
30073
|
};
|
|
29857
30074
|
const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
29858
30075
|
const [width, height] = props.box;
|
|
29859
|
-
const legacyGrid = getLegacyGrid([width, height], props.backgroundImage);
|
|
29860
30076
|
const [state, dispatch] = React__namespace.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
29861
30077
|
React__namespace.useImperativeHandle(ref, () => ({
|
|
29862
30078
|
getUserInput: () => getGradableGraph(state, props.graph)
|
|
@@ -29867,13 +30083,17 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
|
29867
30083
|
height,
|
|
29868
30084
|
position: "relative"
|
|
29869
30085
|
}
|
|
29870
|
-
},
|
|
30086
|
+
}, /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
|
|
30087
|
+
box: props.box,
|
|
30088
|
+
backgroundImage: props.backgroundImage
|
|
30089
|
+
}), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
29871
30090
|
style: {
|
|
29872
30091
|
position: "absolute",
|
|
29873
30092
|
bottom: 0,
|
|
29874
30093
|
left: 0
|
|
29875
30094
|
}
|
|
29876
30095
|
}, /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
|
|
30096
|
+
preserveAspectRatio: false,
|
|
29877
30097
|
viewBox: {
|
|
29878
30098
|
x: props.range[0],
|
|
29879
30099
|
y: props.range[1],
|
|
@@ -29883,7 +30103,7 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
|
29883
30103
|
zoom: false,
|
|
29884
30104
|
width: width,
|
|
29885
30105
|
height: height
|
|
29886
|
-
},
|
|
30106
|
+
}, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement(Grid, props), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
|
|
29887
30107
|
lockedFigures: props.lockedFigures
|
|
29888
30108
|
}), renderGraph({
|
|
29889
30109
|
state,
|
|
@@ -31509,18 +31729,18 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31509
31729
|
const angle = GraphUtils.findAngle(coords[2], coords[0], coords[1]);
|
|
31510
31730
|
return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
|
|
31511
31731
|
}
|
|
31512
|
-
static validate(
|
|
31513
|
-
// TODO(kevinb): rename state to userInput
|
|
31514
|
-
// state === userInput
|
|
31515
|
-
state, rubric, component) {
|
|
31732
|
+
static validate(userInput, rubric, component) {
|
|
31516
31733
|
// When nothing has moved, there will neither be coords nor the
|
|
31517
31734
|
// circle's center/radius fields. When those fields are absent, skip
|
|
31518
31735
|
// all these checks; just go mark the answer as empty.
|
|
31519
|
-
|
|
31520
|
-
|
|
31521
|
-
|
|
31522
|
-
|
|
31523
|
-
|
|
31736
|
+
const hasValue = Boolean(
|
|
31737
|
+
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
31738
|
+
userInput.coords ||
|
|
31739
|
+
// @ts-expect-error - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
31740
|
+
userInput.center && userInput.radius);
|
|
31741
|
+
if (userInput.type === rubric.correct.type && hasValue) {
|
|
31742
|
+
if (userInput.type === "linear" && rubric.correct.type === "linear" && userInput.coords != null) {
|
|
31743
|
+
const guess = userInput.coords;
|
|
31524
31744
|
const correct = rubric.correct.coords;
|
|
31525
31745
|
|
|
31526
31746
|
// If both of the guess points are on the correct line, it's
|
|
@@ -31537,8 +31757,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31537
31757
|
message: null
|
|
31538
31758
|
};
|
|
31539
31759
|
}
|
|
31540
|
-
} else if (
|
|
31541
|
-
const guess =
|
|
31760
|
+
} else if (userInput.type === "linear-system" && rubric.correct.type === "linear-system" && userInput.coords != null) {
|
|
31761
|
+
const guess = userInput.coords;
|
|
31542
31762
|
const correct = rubric.correct.coords;
|
|
31543
31763
|
if (collinear(correct[0][0], correct[0][1], guess[0][0]) && collinear(correct[0][0], correct[0][1], guess[0][1]) && collinear(correct[1][0], correct[1][1], guess[1][0]) && collinear(correct[1][0], correct[1][1], guess[1][1]) || collinear(correct[0][0], correct[0][1], guess[1][0]) && collinear(correct[0][0], correct[0][1], guess[1][1]) && collinear(correct[1][0], correct[1][1], guess[0][0]) && collinear(correct[1][0], correct[1][1], guess[0][1])) {
|
|
31544
31764
|
return {
|
|
@@ -31548,9 +31768,9 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31548
31768
|
message: null
|
|
31549
31769
|
};
|
|
31550
31770
|
}
|
|
31551
|
-
} else if (
|
|
31771
|
+
} else if (userInput.type === "quadratic" && rubric.correct.type === "quadratic" && userInput.coords != null) {
|
|
31552
31772
|
// If the parabola coefficients match, it's correct.
|
|
31553
|
-
const guessCoeffs = this.getQuadraticCoefficients(
|
|
31773
|
+
const guessCoeffs = this.getQuadraticCoefficients(userInput.coords);
|
|
31554
31774
|
const correctCoeffs = this.getQuadraticCoefficients(
|
|
31555
31775
|
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
31556
31776
|
rubric.correct.coords);
|
|
@@ -31562,8 +31782,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31562
31782
|
message: null
|
|
31563
31783
|
};
|
|
31564
31784
|
}
|
|
31565
|
-
} else if (
|
|
31566
|
-
const guessCoeffs = this.getSinusoidCoefficients(
|
|
31785
|
+
} else if (userInput.type === "sinusoid" && rubric.correct.type === "sinusoid" && userInput.coords != null) {
|
|
31786
|
+
const guessCoeffs = this.getSinusoidCoefficients(userInput.coords);
|
|
31567
31787
|
const correctCoeffs = this.getSinusoidCoefficients(
|
|
31568
31788
|
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
31569
31789
|
rubric.correct.coords);
|
|
@@ -31578,8 +31798,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31578
31798
|
message: null
|
|
31579
31799
|
};
|
|
31580
31800
|
}
|
|
31581
|
-
} else if (
|
|
31582
|
-
if (deepEq$1(
|
|
31801
|
+
} else if (userInput.type === "circle" && rubric.correct.type === "circle") {
|
|
31802
|
+
if (deepEq$1(userInput.center, rubric.correct.center) && eq(userInput.radius, rubric.correct.radius)) {
|
|
31583
31803
|
return {
|
|
31584
31804
|
type: "points",
|
|
31585
31805
|
earned: 1,
|
|
@@ -31587,9 +31807,9 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31587
31807
|
message: null
|
|
31588
31808
|
};
|
|
31589
31809
|
}
|
|
31590
|
-
} else if (
|
|
31810
|
+
} else if (userInput.type === "point" && rubric.correct.type === "point" && userInput.coords != null) {
|
|
31591
31811
|
let correct = InteractiveGraph.getPointCoords(rubric.correct, component);
|
|
31592
|
-
const guess =
|
|
31812
|
+
const guess = userInput.coords?.slice();
|
|
31593
31813
|
correct = correct.slice();
|
|
31594
31814
|
// Everything's already rounded so we shouldn't need to do an
|
|
31595
31815
|
// eq() comparison but _.isEqual(0, -0) is false, so we'll use
|
|
@@ -31606,8 +31826,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31606
31826
|
message: null
|
|
31607
31827
|
};
|
|
31608
31828
|
}
|
|
31609
|
-
} else if (
|
|
31610
|
-
const guess =
|
|
31829
|
+
} else if (userInput.type === "polygon" && rubric.correct.type === "polygon" && userInput.coords != null) {
|
|
31830
|
+
const guess = userInput.coords?.slice();
|
|
31611
31831
|
// @ts-expect-error - TS2322 - Type 'Coord[] | undefined' is not assignable to type 'Coord[]'.
|
|
31612
31832
|
const correct = rubric.correct.coords?.slice();
|
|
31613
31833
|
let match;
|
|
@@ -31631,8 +31851,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31631
31851
|
message: null
|
|
31632
31852
|
};
|
|
31633
31853
|
}
|
|
31634
|
-
} else if (
|
|
31635
|
-
let guess =
|
|
31854
|
+
} else if (userInput.type === "segment" && rubric.correct.type === "segment" && userInput.coords != null) {
|
|
31855
|
+
let guess = userInput.coords.slice();
|
|
31636
31856
|
let correct = rubric.correct.coords?.slice();
|
|
31637
31857
|
guess = ___default["default"].invoke(guess, "sort").sort();
|
|
31638
31858
|
// @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
@@ -31645,8 +31865,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31645
31865
|
message: null
|
|
31646
31866
|
};
|
|
31647
31867
|
}
|
|
31648
|
-
} else if (
|
|
31649
|
-
const guess =
|
|
31868
|
+
} else if (userInput.type === "ray" && rubric.correct.type === "ray" && userInput.coords != null) {
|
|
31869
|
+
const guess = userInput.coords;
|
|
31650
31870
|
const correct = rubric.correct.coords;
|
|
31651
31871
|
if (
|
|
31652
31872
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
@@ -31660,8 +31880,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31660
31880
|
message: null
|
|
31661
31881
|
};
|
|
31662
31882
|
}
|
|
31663
|
-
} else if (
|
|
31664
|
-
const guess =
|
|
31883
|
+
} else if (userInput.type === "angle" && rubric.correct.type === "angle") {
|
|
31884
|
+
const guess = userInput.coords;
|
|
31665
31885
|
const correct = rubric.correct.coords;
|
|
31666
31886
|
let match;
|
|
31667
31887
|
if (rubric.correct.match === "congruent") {
|
|
@@ -31700,7 +31920,7 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
31700
31920
|
|
|
31701
31921
|
// The input wasn't correct, so check if it's a blank input or if it's
|
|
31702
31922
|
// actually just wrong
|
|
31703
|
-
if (!hasValue || ___default["default"].isEqual(
|
|
31923
|
+
if (!hasValue || ___default["default"].isEqual(userInput, rubric.graph)) {
|
|
31704
31924
|
// We're where we started.
|
|
31705
31925
|
return {
|
|
31706
31926
|
type: "invalid",
|
|
@@ -32777,7 +32997,7 @@ const styles$g = aphrodite.StyleSheet.create({
|
|
|
32777
32997
|
});
|
|
32778
32998
|
|
|
32779
32999
|
const BringToFront = {
|
|
32780
|
-
boxShadow: `0 8px 8px ${
|
|
33000
|
+
boxShadow: `0 8px 8px ${wonderBlocksTokens.color.offBlack64}`,
|
|
32781
33001
|
zIndex: 1000
|
|
32782
33002
|
};
|
|
32783
33003
|
const AnswerPill = props => {
|
|
@@ -32833,7 +33053,7 @@ const styles$f = aphrodite.StyleSheet.create({
|
|
|
32833
33053
|
backgroundColor: "#00880b"
|
|
32834
33054
|
},
|
|
32835
33055
|
incorrect: {
|
|
32836
|
-
backgroundColor:
|
|
33056
|
+
backgroundColor: wonderBlocksTokens.color.offBlack64
|
|
32837
33057
|
},
|
|
32838
33058
|
pill: {
|
|
32839
33059
|
// Reset the Pill's default height in order to account
|
|
@@ -32877,7 +33097,7 @@ class Marker extends React__namespace.Component {
|
|
|
32877
33097
|
// default dot
|
|
32878
33098
|
let args = {
|
|
32879
33099
|
size: MARKER_SIZE,
|
|
32880
|
-
color:
|
|
33100
|
+
color: wonderBlocksTokens.color.white,
|
|
32881
33101
|
icon: iconNull
|
|
32882
33102
|
};
|
|
32883
33103
|
if (showCorrectness) {
|
|
@@ -32938,7 +33158,7 @@ class Marker extends React__namespace.Component {
|
|
|
32938
33158
|
const styles$e = aphrodite.StyleSheet.create({
|
|
32939
33159
|
marker: {
|
|
32940
33160
|
position: "absolute",
|
|
32941
|
-
backgroundColor:
|
|
33161
|
+
backgroundColor: wonderBlocksTokens.color.white,
|
|
32942
33162
|
borderRadius: MARKER_SIZE,
|
|
32943
33163
|
// Center marker position based on its maximum size.
|
|
32944
33164
|
width: MARKER_SIZE,
|
|
@@ -32946,7 +33166,7 @@ const styles$e = aphrodite.StyleSheet.create({
|
|
|
32946
33166
|
marginLeft: MARKER_SIZE / -2,
|
|
32947
33167
|
marginTop: MARKER_SIZE / -2,
|
|
32948
33168
|
// Add a shadow to the marker to make it stand out from the image.
|
|
32949
|
-
boxShadow: `0 8px 8px ${
|
|
33169
|
+
boxShadow: `0 8px 8px ${wonderBlocksTokens.color.offBlack8}`
|
|
32950
33170
|
},
|
|
32951
33171
|
// The base and unfilled marker style.
|
|
32952
33172
|
markerIcon: {
|
|
@@ -32956,7 +33176,7 @@ const styles$e = aphrodite.StyleSheet.create({
|
|
|
32956
33176
|
boxSizing: "border-box",
|
|
32957
33177
|
width: MARKER_SIZE,
|
|
32958
33178
|
height: MARKER_SIZE,
|
|
32959
|
-
border: `2px solid ${
|
|
33179
|
+
border: `2px solid ${wonderBlocksTokens.color.offBlack64}`,
|
|
32960
33180
|
borderRadius: MARKER_SIZE
|
|
32961
33181
|
},
|
|
32962
33182
|
// The animation that presents the marker to the learner
|
|
@@ -32964,11 +33184,11 @@ const styles$e = aphrodite.StyleSheet.create({
|
|
|
32964
33184
|
animationName: {
|
|
32965
33185
|
"0%": {
|
|
32966
33186
|
transform: "scale(1)",
|
|
32967
|
-
backgroundColor:
|
|
33187
|
+
backgroundColor: wonderBlocksTokens.color.blue
|
|
32968
33188
|
},
|
|
32969
33189
|
"100%": {
|
|
32970
33190
|
transform: "scale(1.3)",
|
|
32971
|
-
backgroundColor:
|
|
33191
|
+
backgroundColor: wonderBlocksTokens.color.blue
|
|
32972
33192
|
}
|
|
32973
33193
|
},
|
|
32974
33194
|
animationDirection: "alternate",
|
|
@@ -32985,35 +33205,35 @@ const styles$e = aphrodite.StyleSheet.create({
|
|
|
32985
33205
|
animationIterationCount: "2"
|
|
32986
33206
|
},
|
|
32987
33207
|
markerActive: {
|
|
32988
|
-
outline: `2px solid ${
|
|
33208
|
+
outline: `2px solid ${wonderBlocksTokens.color.blue}`,
|
|
32989
33209
|
outlineOffset: 2
|
|
32990
33210
|
},
|
|
32991
33211
|
// The learner is making an initial selection
|
|
32992
33212
|
markerSelected: {
|
|
32993
|
-
boxShadow: `0 8px 8px ${
|
|
32994
|
-
border: `solid 4px ${
|
|
32995
|
-
backgroundColor:
|
|
33213
|
+
boxShadow: `0 8px 8px ${wonderBlocksTokens.color.offBlack8}`,
|
|
33214
|
+
border: `solid 4px ${wonderBlocksTokens.color.white}`,
|
|
33215
|
+
backgroundColor: wonderBlocksTokens.color.blue,
|
|
32996
33216
|
borderRadius: MARKER_SIZE,
|
|
32997
33217
|
transform: "rotate(180deg)"
|
|
32998
33218
|
},
|
|
32999
33219
|
// The learner has made a selection
|
|
33000
33220
|
markerFilled: {
|
|
33001
33221
|
backgroundColor: "#ECF3FE",
|
|
33002
|
-
border: `4px solid ${
|
|
33222
|
+
border: `4px solid ${wonderBlocksTokens.color.blue}`
|
|
33003
33223
|
},
|
|
33004
33224
|
markerGraded: {
|
|
33005
33225
|
width: MARKER_SIZE,
|
|
33006
33226
|
height: MARKER_SIZE,
|
|
33007
33227
|
justifyContent: "center",
|
|
33008
33228
|
alignItems: "center",
|
|
33009
|
-
border: `2px solid ${
|
|
33229
|
+
border: `2px solid ${wonderBlocksTokens.color.white}`
|
|
33010
33230
|
},
|
|
33011
33231
|
markerCorrect: {
|
|
33012
33232
|
background: "#00880b" // WB green darkened by 18%
|
|
33013
33233
|
},
|
|
33014
33234
|
|
|
33015
33235
|
markerIncorrect: {
|
|
33016
|
-
background:
|
|
33236
|
+
background: wonderBlocksTokens.color.offBlack64
|
|
33017
33237
|
}
|
|
33018
33238
|
});
|
|
33019
33239
|
|
|
@@ -33444,6 +33664,7 @@ class LabelImage extends React__namespace.Component {
|
|
|
33444
33664
|
const answerChoicesActive = index === activeMarkerIndex;
|
|
33445
33665
|
const showAnswerChoice = marker.selected && !answerChoicesActive && !this.state.hideAnswers;
|
|
33446
33666
|
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
33667
|
+
key: index,
|
|
33447
33668
|
style: {
|
|
33448
33669
|
position: "absolute",
|
|
33449
33670
|
left: `${marker.x}%`,
|
|
@@ -38048,7 +38269,7 @@ const styles$8 = aphrodite.StyleSheet.create({
|
|
|
38048
38269
|
// and the "Add highlight" action fails.
|
|
38049
38270
|
userSelect: "none",
|
|
38050
38271
|
fontFamily: `"Lato", sans-serif`,
|
|
38051
|
-
padding: `10px ${
|
|
38272
|
+
padding: `10px ${wonderBlocksTokens.spacing.medium_16}px`,
|
|
38052
38273
|
cursor: "pointer"
|
|
38053
38274
|
}
|
|
38054
38275
|
});
|
|
@@ -41894,7 +42115,7 @@ const init = function (options) {
|
|
|
41894
42115
|
|
|
41895
42116
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
41896
42117
|
const libName = "@khanacademy/perseus";
|
|
41897
|
-
const libVersion = "
|
|
42118
|
+
const libVersion = "21.0.0";
|
|
41898
42119
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
41899
42120
|
|
|
41900
42121
|
const apiVersion = {
|
|
@@ -44143,6 +44364,138 @@ const registerAllWidgetsForTesting = () => {
|
|
|
44143
44364
|
replaceDeprecatedWidgets();
|
|
44144
44365
|
};
|
|
44145
44366
|
|
|
44367
|
+
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
44368
|
+
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
44369
|
+
|
|
44370
|
+
/**
|
|
44371
|
+
* Regex for widget placeholders in a string.
|
|
44372
|
+
*
|
|
44373
|
+
* First capture group is the widget ID (ex. 'radio 1')
|
|
44374
|
+
* Second capture group is the widget type (ex. "radio)
|
|
44375
|
+
* exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
|
|
44376
|
+
*/
|
|
44377
|
+
function getWidgetRegex() {
|
|
44378
|
+
return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g;
|
|
44379
|
+
}
|
|
44380
|
+
|
|
44381
|
+
/**
|
|
44382
|
+
* Add a widget placeholder using the widget ID.
|
|
44383
|
+
* ex. addWidget("radio 1") => "[[☃ radio 1]]"
|
|
44384
|
+
*
|
|
44385
|
+
* @param {string} id
|
|
44386
|
+
* @returns {string}
|
|
44387
|
+
*/
|
|
44388
|
+
function addWidget(id) {
|
|
44389
|
+
return `[[☃ ${id}]]`;
|
|
44390
|
+
}
|
|
44391
|
+
|
|
44392
|
+
/**
|
|
44393
|
+
* Get a widget type by a widget's ID
|
|
44394
|
+
* (please don't derive type from ID)
|
|
44395
|
+
*
|
|
44396
|
+
* @param {string} widgetId the ID of the widget
|
|
44397
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
44398
|
+
* @returns {string} the widget type (ie "radio")
|
|
44399
|
+
*/
|
|
44400
|
+
function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
|
|
44401
|
+
const widget = widgetMap[WidgetId];
|
|
44402
|
+
return widget?.type ?? null;
|
|
44403
|
+
}
|
|
44404
|
+
|
|
44405
|
+
/**
|
|
44406
|
+
* Does the content have a specific type of widget?
|
|
44407
|
+
*
|
|
44408
|
+
* @param {string} type the type of the widget in question (ie "radio")
|
|
44409
|
+
* @param {string} content the string to search through
|
|
44410
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
44411
|
+
* @returns {boolean} if the content includes the widget type
|
|
44412
|
+
*/
|
|
44413
|
+
function contentHasWidgetType(type, content, widgetMap) {
|
|
44414
|
+
return getWidgetIdsFromContentByType(type, content, widgetMap).length > 0;
|
|
44415
|
+
}
|
|
44416
|
+
|
|
44417
|
+
/**
|
|
44418
|
+
* Extract all widget IDs, which includes the widget type and instance number.
|
|
44419
|
+
* example output: ['radio 1', 'categorizer 1', 'categorizor 2']
|
|
44420
|
+
*
|
|
44421
|
+
* Content should contain Perseus widget placeholders,
|
|
44422
|
+
* which look like: '[[☃ radio 1]]'.
|
|
44423
|
+
*
|
|
44424
|
+
* @param {string} content
|
|
44425
|
+
* @returns {ReadonlyArray<string>} widgetIds
|
|
44426
|
+
*/
|
|
44427
|
+
function getWidgetIdsFromContent(content) {
|
|
44428
|
+
const widgets = [];
|
|
44429
|
+
const localWidgetRegex = getWidgetRegex();
|
|
44430
|
+
let match = localWidgetRegex.exec(content);
|
|
44431
|
+
while (match !== null) {
|
|
44432
|
+
widgets.push(match[1]);
|
|
44433
|
+
match = localWidgetRegex.exec(content);
|
|
44434
|
+
}
|
|
44435
|
+
return widgets;
|
|
44436
|
+
}
|
|
44437
|
+
|
|
44438
|
+
/**
|
|
44439
|
+
* Get a list of widget IDs from content,
|
|
44440
|
+
* but only for specific widget types
|
|
44441
|
+
*
|
|
44442
|
+
* @param {string} type the type of widget (ie "radio")
|
|
44443
|
+
* @param {string} content the string to parse
|
|
44444
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
44445
|
+
* @returns {ReadonlyArray<string>} the widget type (ie "radio")
|
|
44446
|
+
*/
|
|
44447
|
+
function getWidgetIdsFromContentByType(type, content, widgetMap) {
|
|
44448
|
+
const rv = [];
|
|
44449
|
+
const widgetIdsInContent = getWidgetIdsFromContent(content);
|
|
44450
|
+
widgetIdsInContent.forEach(widgetId => {
|
|
44451
|
+
const widget = widgetMap[widgetId];
|
|
44452
|
+
if (widget?.type === type) {
|
|
44453
|
+
rv.push(widgetId);
|
|
44454
|
+
}
|
|
44455
|
+
});
|
|
44456
|
+
return rv;
|
|
44457
|
+
}
|
|
44458
|
+
|
|
44459
|
+
/**
|
|
44460
|
+
* Pull the widget map out of ItemData
|
|
44461
|
+
*
|
|
44462
|
+
* @param {PerseusItem} PerseusItem containing a widgetMap
|
|
44463
|
+
* @returns {WidgetMap} the widget map in the PerseusItem
|
|
44464
|
+
*/
|
|
44465
|
+
function getWidgetsMapFromItemData(itemData) {
|
|
44466
|
+
return itemData.question.widgets;
|
|
44467
|
+
}
|
|
44468
|
+
|
|
44469
|
+
/**
|
|
44470
|
+
* Get the widget information from a WidgetMap
|
|
44471
|
+
*
|
|
44472
|
+
* @param {string} widgetId the ID of the widget
|
|
44473
|
+
* @returns {PerseusWidget | null} the widget data if it exists, otherwise null
|
|
44474
|
+
*/
|
|
44475
|
+
function getWidgetFromWidgetMap(widgetId, widgetMap) {
|
|
44476
|
+
return widgetMap[widgetId] ?? null;
|
|
44477
|
+
}
|
|
44478
|
+
|
|
44479
|
+
/**
|
|
44480
|
+
* Get select widgets from a widget map.
|
|
44481
|
+
* Useful for multi-items that needs to split
|
|
44482
|
+
* one PerseusItem into several
|
|
44483
|
+
*
|
|
44484
|
+
* @param {ReadonlyArray<string>} widgetIds to extract
|
|
44485
|
+
* @param {PerseusWidgetsMap} widgetMap to extract from
|
|
44486
|
+
* @return {PerseusWidgetsMap} a new widget map with requested widgets
|
|
44487
|
+
*/
|
|
44488
|
+
function getWidgetsFromWidgetMap(widgetIds, widgetMap) {
|
|
44489
|
+
const widgets = {};
|
|
44490
|
+
widgetIds.forEach(widgetId => {
|
|
44491
|
+
const widget = getWidgetFromWidgetMap(widgetId, widgetMap);
|
|
44492
|
+
if (widget) {
|
|
44493
|
+
widgets[widgetId] = widget;
|
|
44494
|
+
}
|
|
44495
|
+
});
|
|
44496
|
+
return widgets;
|
|
44497
|
+
}
|
|
44498
|
+
|
|
44146
44499
|
/**
|
|
44147
44500
|
* This function extracts the answers from the widgets.
|
|
44148
44501
|
*
|
|
@@ -44547,18 +44900,15 @@ const INDIVIDUAL_ANSWER_WIDGETS = ["interactive-graph", "categorizer", "grapher"
|
|
|
44547
44900
|
|
|
44548
44901
|
/* Widgets that are supported for automatic scoring */
|
|
44549
44902
|
const SUPPORTED_WIDGETS = ["radio", "numeric-input", "input-number", "expression", ...INDIVIDUAL_ANSWER_WIDGETS];
|
|
44550
|
-
const getWidgetTypeFromWidgetKey = widgetKey => {
|
|
44551
|
-
return widgetKey.split(" ")[0];
|
|
44552
|
-
};
|
|
44553
44903
|
|
|
44554
44904
|
/* Verify if the perseus item has supported widgets for automatic scoring */
|
|
44555
|
-
const isWrongAnswerSupported =
|
|
44556
|
-
return
|
|
44905
|
+
const isWrongAnswerSupported = (widgetIds, widgetMap) => {
|
|
44906
|
+
return widgetIds.length !== 0 && widgetIds.every(widgetId => SUPPORTED_WIDGETS.includes(getWidgetTypeByWidgetId(widgetId, widgetMap)));
|
|
44557
44907
|
};
|
|
44558
44908
|
|
|
44559
|
-
/* Verify if the widget
|
|
44560
|
-
const shouldHaveIndividualAnswer =
|
|
44561
|
-
return INDIVIDUAL_ANSWER_WIDGETS.includes(
|
|
44909
|
+
/* Verify if the widget ID has an individual answer for the coach report view */
|
|
44910
|
+
const shouldHaveIndividualAnswer = (widgetId, widgetMap) => {
|
|
44911
|
+
return INDIVIDUAL_ANSWER_WIDGETS.includes(getWidgetTypeByWidgetId(widgetId, widgetMap));
|
|
44562
44912
|
};
|
|
44563
44913
|
|
|
44564
44914
|
/* Returns the answer userInput submission for currently supported widgets */
|
|
@@ -44577,29 +44927,30 @@ const getAnswerFromUserInput = (widgetType, userInput) => {
|
|
|
44577
44927
|
return userInput;
|
|
44578
44928
|
};
|
|
44579
44929
|
|
|
44580
|
-
/* Returns the correct answer for a given widget
|
|
44930
|
+
/* Returns the correct answer for a given widget ID and Perseus Item */
|
|
44581
44931
|
// TODO (LEMS-1835): We should fix the resonse type from getWidget to be specific.
|
|
44582
44932
|
// TODO (LEMS-1836): We should also consider adding the getOneCorrectAnswerFromRubric method to all widgets.
|
|
44583
|
-
const
|
|
44584
|
-
const rubric = itemData.question.widgets[
|
|
44585
|
-
const
|
|
44933
|
+
const getCorrectAnswerForWidgetId = (widgetId, itemData) => {
|
|
44934
|
+
const rubric = itemData.question.widgets[widgetId].options;
|
|
44935
|
+
const widgetMap = getWidgetsMapFromItemData(itemData);
|
|
44936
|
+
const widgetType = getWidgetTypeByWidgetId(widgetId, widgetMap);
|
|
44586
44937
|
const widget = getWidget(widgetType);
|
|
44587
44938
|
|
|
44588
44939
|
// @ts-expect-error - TS2339 - Property 'getOneCorrectAnswerFromRubric' does not exist on type 'ComponentType<any>'.
|
|
44589
44940
|
return widget?.getOneCorrectAnswerFromRubric?.(rubric);
|
|
44590
44941
|
};
|
|
44591
44942
|
|
|
44592
|
-
/* Verify if the widget
|
|
44593
|
-
const
|
|
44594
|
-
return perseusItem.question.content.indexOf(
|
|
44943
|
+
/* Verify if the widget ID exists in the content string of the Perseus Item */
|
|
44944
|
+
const isWidgetIdInContent = (perseusItem, widgetId) => {
|
|
44945
|
+
return perseusItem.question.content.indexOf(widgetId) !== -1;
|
|
44595
44946
|
};
|
|
44596
44947
|
|
|
44597
|
-
/* Return an array of all the widget
|
|
44598
|
-
const
|
|
44948
|
+
/* Return an array of all the widget IDs that exist in the content string of a Perseus Item */
|
|
44949
|
+
const getValidWidgetIds = perseusItem => {
|
|
44599
44950
|
const {
|
|
44600
44951
|
widgets
|
|
44601
44952
|
} = perseusItem.question;
|
|
44602
|
-
return wonderStuffCore.keys(widgets).filter(
|
|
44953
|
+
return wonderStuffCore.keys(widgets).filter(id => isWidgetIdInContent(perseusItem, id));
|
|
44603
44954
|
};
|
|
44604
44955
|
|
|
44605
44956
|
/**
|
|
@@ -44647,7 +44998,7 @@ const genericPerseusItemData = {
|
|
|
44647
44998
|
* uses a basic Perseus object and updates it with custom values as needed.
|
|
44648
44999
|
*
|
|
44649
45000
|
* @param {Partial<PerseusItem>} customFields
|
|
44650
|
-
* @returns PerseusItem
|
|
45001
|
+
* @returns {PerseusItem}
|
|
44651
45002
|
*/
|
|
44652
45003
|
function generateTestPerseusItem() {
|
|
44653
45004
|
let customFields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -44657,6 +45008,107 @@ function generateTestPerseusItem() {
|
|
|
44657
45008
|
};
|
|
44658
45009
|
}
|
|
44659
45010
|
|
|
45011
|
+
/**
|
|
45012
|
+
* Creates an object with the bare amount
|
|
45013
|
+
* of data to be a properly typed RadioWidget
|
|
45014
|
+
*
|
|
45015
|
+
* @returns {RadioWidget} skeleton RadioWidget for testing
|
|
45016
|
+
*/
|
|
45017
|
+
function generateTestRadioWidget() {
|
|
45018
|
+
return {
|
|
45019
|
+
type: "radio",
|
|
45020
|
+
options: {
|
|
45021
|
+
choices: []
|
|
45022
|
+
}
|
|
45023
|
+
};
|
|
45024
|
+
}
|
|
45025
|
+
|
|
45026
|
+
/**
|
|
45027
|
+
* Creates an object with the bare amount
|
|
45028
|
+
* of data to be a properly typed InteractiveGraphWidget
|
|
45029
|
+
*
|
|
45030
|
+
* @returns {InteractiveGraphWidget} skeleton InteractiveGraphWidget for testing
|
|
45031
|
+
*/
|
|
45032
|
+
function generateTestInteractiveGraphWidget() {
|
|
45033
|
+
return {
|
|
45034
|
+
type: "interactive-graph",
|
|
45035
|
+
options: {
|
|
45036
|
+
step: [1, 1],
|
|
45037
|
+
gridStep: [1, 1],
|
|
45038
|
+
snapStep: [1, 1],
|
|
45039
|
+
markings: "graph",
|
|
45040
|
+
labels: ["x", "y"],
|
|
45041
|
+
showProtractor: false,
|
|
45042
|
+
showRuler: false,
|
|
45043
|
+
rulerLabel: "mm",
|
|
45044
|
+
rulerTicks: 2,
|
|
45045
|
+
range: [[-10, 10], [-10, 10]],
|
|
45046
|
+
graph: {
|
|
45047
|
+
type: "angle"
|
|
45048
|
+
},
|
|
45049
|
+
correct: {
|
|
45050
|
+
type: "angle"
|
|
45051
|
+
}
|
|
45052
|
+
}
|
|
45053
|
+
};
|
|
45054
|
+
}
|
|
45055
|
+
|
|
45056
|
+
/**
|
|
45057
|
+
* Creates an object with the bare amount
|
|
45058
|
+
* of data to be a properly typed InteractiveGraphWidget
|
|
45059
|
+
*
|
|
45060
|
+
* @returns {InteractiveGraphWidget} skeleton InteractiveGraphWidget for testing
|
|
45061
|
+
*/
|
|
45062
|
+
function generateTestCategorizerWidget() {
|
|
45063
|
+
return {
|
|
45064
|
+
type: "categorizer",
|
|
45065
|
+
options: {
|
|
45066
|
+
items: [],
|
|
45067
|
+
categories: [],
|
|
45068
|
+
randomizeItems: false,
|
|
45069
|
+
static: false,
|
|
45070
|
+
values: []
|
|
45071
|
+
}
|
|
45072
|
+
};
|
|
45073
|
+
}
|
|
45074
|
+
|
|
45075
|
+
/**
|
|
45076
|
+
* Creates an object with the bare amount
|
|
45077
|
+
* of data to be a properly typed ExpressionWidget
|
|
45078
|
+
*
|
|
45079
|
+
* @returns {ExpressionWidget} skeleton ExpressionWidget for testing
|
|
45080
|
+
*/
|
|
45081
|
+
function generateTestExpressionWidget() {
|
|
45082
|
+
return {
|
|
45083
|
+
type: "expression",
|
|
45084
|
+
options: {
|
|
45085
|
+
answerForms: [],
|
|
45086
|
+
buttonSets: ["basic"],
|
|
45087
|
+
functions: [],
|
|
45088
|
+
times: false
|
|
45089
|
+
}
|
|
45090
|
+
};
|
|
45091
|
+
}
|
|
45092
|
+
|
|
45093
|
+
/**
|
|
45094
|
+
* Creates an object with the bare amount
|
|
45095
|
+
* of data to be a properly typed NumericInputWidget
|
|
45096
|
+
*
|
|
45097
|
+
* @returns {NumericInputWidget} skeleton NumericInputWidget for testing
|
|
45098
|
+
*/
|
|
45099
|
+
function generateTestNumericInputWidget() {
|
|
45100
|
+
return {
|
|
45101
|
+
type: "numeric-input",
|
|
45102
|
+
options: {
|
|
45103
|
+
answers: [],
|
|
45104
|
+
labelText: "",
|
|
45105
|
+
size: "normal",
|
|
45106
|
+
coefficient: false,
|
|
45107
|
+
static: false
|
|
45108
|
+
}
|
|
45109
|
+
};
|
|
45110
|
+
}
|
|
45111
|
+
|
|
44660
45112
|
/**
|
|
44661
45113
|
* Some helpers for CEDAR logging. CEDAR convention dictates
|
|
44662
45114
|
* that enum values should be upper case. Ex: for the
|
|
@@ -44711,163 +45163,6 @@ function convertWidgetNameToEnum(name) {
|
|
|
44711
45163
|
}
|
|
44712
45164
|
return widgetEnum;
|
|
44713
45165
|
}
|
|
44714
|
-
function getWidgetTypeFromWidgetId(widgetId) {
|
|
44715
|
-
const [widgetType, widgetNumber] = widgetId.split(" ");
|
|
44716
|
-
if (!widgetNumber) {
|
|
44717
|
-
throw new Error(`Incorrect format for widget ID: ${widgetId}`);
|
|
44718
|
-
}
|
|
44719
|
-
if (!widgetNameToEnum[widgetType]) {
|
|
44720
|
-
throw new Error(`Unknown widget type (${widgetType}) in widget ID: ${widgetId}`);
|
|
44721
|
-
}
|
|
44722
|
-
return widgetType;
|
|
44723
|
-
}
|
|
44724
|
-
|
|
44725
|
-
let WidgetType = /*#__PURE__*/function (WidgetType) {
|
|
44726
|
-
WidgetType["Categorizer"] = "categorizer";
|
|
44727
|
-
WidgetType["CSProgram"] = "cs-program";
|
|
44728
|
-
WidgetType["Definition"] = "definition";
|
|
44729
|
-
WidgetType["Dropdown"] = "dropdown";
|
|
44730
|
-
WidgetType["Explanation"] = "explanation";
|
|
44731
|
-
WidgetType["Expression"] = "expression";
|
|
44732
|
-
WidgetType["GradedGroup"] = "graded-group";
|
|
44733
|
-
WidgetType["GradedGroupAnswerBar"] = "graded-group-answer-bar";
|
|
44734
|
-
WidgetType["GradedGroupSet"] = "graded-group-set";
|
|
44735
|
-
WidgetType["Grapher"] = "grapher";
|
|
44736
|
-
WidgetType["Group"] = "group";
|
|
44737
|
-
WidgetType["Iframe"] = "iframe";
|
|
44738
|
-
WidgetType["Image"] = "image";
|
|
44739
|
-
WidgetType["InputNumber"] = "input-number";
|
|
44740
|
-
WidgetType["Interaction"] = "interaction";
|
|
44741
|
-
WidgetType["InteractiveGraph"] = "interactive-graph";
|
|
44742
|
-
WidgetType["InteractiveNumberLine"] = "interactive-number-line";
|
|
44743
|
-
WidgetType["LabelImage"] = "label-image";
|
|
44744
|
-
WidgetType["Matcher"] = "matcher";
|
|
44745
|
-
WidgetType["Matrix"] = "matrix";
|
|
44746
|
-
WidgetType["Measurer"] = "measurer";
|
|
44747
|
-
WidgetType["Molecule"] = "molecule";
|
|
44748
|
-
WidgetType["NumberLine"] = "number-line";
|
|
44749
|
-
WidgetType["NumericInput"] = "numeric-input";
|
|
44750
|
-
WidgetType["Orderer"] = "orderer";
|
|
44751
|
-
WidgetType["Passage"] = "passage";
|
|
44752
|
-
WidgetType["PassageRef"] = "passage-ref";
|
|
44753
|
-
WidgetType["PassageRefTarget"] = "passage-ref-target";
|
|
44754
|
-
WidgetType["Plotter"] = "plotter";
|
|
44755
|
-
WidgetType["Protractor"] = "protractor";
|
|
44756
|
-
WidgetType["PythonProgram"] = "python-program";
|
|
44757
|
-
WidgetType["Radio"] = "radio";
|
|
44758
|
-
WidgetType["Sorter"] = "sorter";
|
|
44759
|
-
WidgetType["Table"] = "table";
|
|
44760
|
-
WidgetType["Unit"] = "unit";
|
|
44761
|
-
WidgetType["Video"] = "video";
|
|
44762
|
-
WidgetType["VideoTranscriptLink"] = "video-transcript-link";
|
|
44763
|
-
return WidgetType;
|
|
44764
|
-
}({});
|
|
44765
|
-
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
44766
|
-
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
44767
|
-
|
|
44768
|
-
/**
|
|
44769
|
-
* Regex for widget placeholders in a string.
|
|
44770
|
-
*
|
|
44771
|
-
* First capture group is the widget ID (ex. 'radio 1')
|
|
44772
|
-
* Second capture group is the widget type (ex. "radio)
|
|
44773
|
-
* exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
|
|
44774
|
-
*/
|
|
44775
|
-
function getWidgetRegex() {
|
|
44776
|
-
return /\[\[☃ (([a-z-]+) \d+)\]\]/g;
|
|
44777
|
-
}
|
|
44778
|
-
|
|
44779
|
-
/**
|
|
44780
|
-
* Add a widget placeholder using the provided widget type and instance number.
|
|
44781
|
-
* ex. addWidget(WidgetType.Radio, 1) => "[[☃ radio 1]]"
|
|
44782
|
-
*
|
|
44783
|
-
* @param {WidgetType} widgetType
|
|
44784
|
-
* @param {number} instance
|
|
44785
|
-
* @returns {string}
|
|
44786
|
-
*/
|
|
44787
|
-
function addWidget(widgetType, instance) {
|
|
44788
|
-
return `[[☃ ${widgetType} ${String(instance)}]]`;
|
|
44789
|
-
}
|
|
44790
|
-
|
|
44791
|
-
/**
|
|
44792
|
-
* Extract all widget IDs, which includes the widget type and instance number.
|
|
44793
|
-
* example output: ['radio 1', 'categorizer 1', 'categorizor 2']
|
|
44794
|
-
*
|
|
44795
|
-
* Content should contain Perseus widget placeholders,
|
|
44796
|
-
* which look like: '[[☃ radio 1]]'.
|
|
44797
|
-
*
|
|
44798
|
-
* @param {string} content
|
|
44799
|
-
* @returns {Array<WidgetId>} widgets
|
|
44800
|
-
*/
|
|
44801
|
-
function getAllWidgetIds(content) {
|
|
44802
|
-
const widgets = [];
|
|
44803
|
-
const localWidgetRegex = getWidgetRegex();
|
|
44804
|
-
let match = localWidgetRegex.exec(content);
|
|
44805
|
-
while (match !== null) {
|
|
44806
|
-
widgets.push(match[1]);
|
|
44807
|
-
match = localWidgetRegex.exec(content);
|
|
44808
|
-
}
|
|
44809
|
-
return widgets;
|
|
44810
|
-
}
|
|
44811
|
-
|
|
44812
|
-
/**
|
|
44813
|
-
* Check if a specific widget is an actual widget type using its name.
|
|
44814
|
-
* The widget name should include a widget type, without an instance number,
|
|
44815
|
-
* as a string. ex. 'radio'
|
|
44816
|
-
*
|
|
44817
|
-
* @param {string} widgetName
|
|
44818
|
-
* @returns {boolean}
|
|
44819
|
-
*/
|
|
44820
|
-
function isWidgetType(widgetName) {
|
|
44821
|
-
return Object.values(WidgetType).includes(widgetName);
|
|
44822
|
-
}
|
|
44823
|
-
|
|
44824
|
-
/**
|
|
44825
|
-
* Extract all widget types from a Perseus JSON content string.
|
|
44826
|
-
* This does not include the instance number and prevents duplicates.
|
|
44827
|
-
* example output: ['radio', 'categorizer']
|
|
44828
|
-
*
|
|
44829
|
-
* @param {string} content
|
|
44830
|
-
* @returns {Array<WidgetId>}
|
|
44831
|
-
*/
|
|
44832
|
-
function getAllWidgetTypes(content) {
|
|
44833
|
-
const widgetTypes = [];
|
|
44834
|
-
const localWidgetRegex = getWidgetRegex();
|
|
44835
|
-
let match = localWidgetRegex.exec(content);
|
|
44836
|
-
while (match !== null) {
|
|
44837
|
-
const matchType = match[2];
|
|
44838
|
-
if (!widgetTypes.includes(matchType) && isWidgetType(matchType)) {
|
|
44839
|
-
widgetTypes.push(matchType);
|
|
44840
|
-
}
|
|
44841
|
-
match = localWidgetRegex.exec(content);
|
|
44842
|
-
}
|
|
44843
|
-
return widgetTypes;
|
|
44844
|
-
}
|
|
44845
|
-
|
|
44846
|
-
/**
|
|
44847
|
-
* Check if a specific widget is a question widget type using its ID.
|
|
44848
|
-
* The widget ID includes the widget type and the instance number.
|
|
44849
|
-
*
|
|
44850
|
-
* @param {WidgetId} widgetId
|
|
44851
|
-
* @returns {boolean}
|
|
44852
|
-
*/
|
|
44853
|
-
function isQuestionWidgetType(widgetId) {
|
|
44854
|
-
return QUESTION_WIDGETS.includes(widgetId.split(" ")[0]);
|
|
44855
|
-
}
|
|
44856
|
-
|
|
44857
|
-
/**
|
|
44858
|
-
* Extract the widget IDs of all widgets considered question widgets. Widget IDs
|
|
44859
|
-
* include widget type and instance number.
|
|
44860
|
-
* example output: ['radio 1', 'categorizer 1']
|
|
44861
|
-
*
|
|
44862
|
-
* Content should contain Perseus widget placeholders,
|
|
44863
|
-
* which look like: '[[☃ radio 1]]'.
|
|
44864
|
-
*
|
|
44865
|
-
* @param {string} content
|
|
44866
|
-
* @returns Array<WidgetId>
|
|
44867
|
-
*/
|
|
44868
|
-
function getQuestionWidgetIds(content) {
|
|
44869
|
-
return getAllWidgetIds(content).filter(isQuestionWidgetType);
|
|
44870
|
-
}
|
|
44871
45166
|
|
|
44872
45167
|
const EditorJsonify = {
|
|
44873
45168
|
serialize: function () {
|
|
@@ -44919,7 +45214,6 @@ exports.TableWidget = Table$1;
|
|
|
44919
45214
|
exports.Util = Util;
|
|
44920
45215
|
exports.WIDGET_PROP_DENYLIST = denylist;
|
|
44921
45216
|
exports.WidgetJsonifyDeprecated = WidgetJsonifyDeprecated;
|
|
44922
|
-
exports.WidgetType = WidgetType;
|
|
44923
45217
|
exports.Widgets = widgets$1;
|
|
44924
45218
|
exports.addWidget = addWidget;
|
|
44925
45219
|
exports.apiVersion = apiVersion;
|
|
@@ -44927,19 +45221,26 @@ exports.buildEmptyItemTreeForShape = buildEmptyItemTreeForShape;
|
|
|
44927
45221
|
exports.buildMapper = buildMapper;
|
|
44928
45222
|
exports.components = components;
|
|
44929
45223
|
exports.constants = constants;
|
|
45224
|
+
exports.contentHasWidgetType = contentHasWidgetType;
|
|
44930
45225
|
exports.convertWidgetNameToEnum = convertWidgetNameToEnum;
|
|
44931
45226
|
exports.displaySigFigs = displaySigFigs;
|
|
45227
|
+
exports.generateTestCategorizerWidget = generateTestCategorizerWidget;
|
|
45228
|
+
exports.generateTestExpressionWidget = generateTestExpressionWidget;
|
|
45229
|
+
exports.generateTestInteractiveGraphWidget = generateTestInteractiveGraphWidget;
|
|
45230
|
+
exports.generateTestNumericInputWidget = generateTestNumericInputWidget;
|
|
44932
45231
|
exports.generateTestPerseusItem = generateTestPerseusItem;
|
|
45232
|
+
exports.generateTestRadioWidget = generateTestRadioWidget;
|
|
44933
45233
|
exports.genericPerseusItemData = genericPerseusItemData;
|
|
44934
|
-
exports.getAllWidgetIds = getAllWidgetIds;
|
|
44935
|
-
exports.getAllWidgetTypes = getAllWidgetTypes;
|
|
44936
45234
|
exports.getAnswerFromUserInput = getAnswerFromUserInput;
|
|
44937
45235
|
exports.getAnswersFromWidgets = getAnswersFromWidgets;
|
|
44938
|
-
exports.
|
|
44939
|
-
exports.
|
|
44940
|
-
exports.
|
|
44941
|
-
exports.
|
|
44942
|
-
exports.
|
|
45236
|
+
exports.getCorrectAnswerForWidgetId = getCorrectAnswerForWidgetId;
|
|
45237
|
+
exports.getValidWidgetIds = getValidWidgetIds;
|
|
45238
|
+
exports.getWidgetFromWidgetMap = getWidgetFromWidgetMap;
|
|
45239
|
+
exports.getWidgetIdsFromContent = getWidgetIdsFromContent;
|
|
45240
|
+
exports.getWidgetIdsFromContentByType = getWidgetIdsFromContentByType;
|
|
45241
|
+
exports.getWidgetTypeByWidgetId = getWidgetTypeByWidgetId;
|
|
45242
|
+
exports.getWidgetsFromWidgetMap = getWidgetsFromWidgetMap;
|
|
45243
|
+
exports.getWidgetsMapFromItemData = getWidgetsMapFromItemData;
|
|
44943
45244
|
exports.globalConstants = globalConstants;
|
|
44944
45245
|
exports.globalStyles = globalStyles;
|
|
44945
45246
|
exports.icons = iconPaths;
|
|
@@ -44947,7 +45248,7 @@ exports.init = init;
|
|
|
44947
45248
|
exports.injectWidgets = injectWidgets;
|
|
44948
45249
|
exports.interactiveSizes = interactiveSizes$1;
|
|
44949
45250
|
exports.isItemRenderableByVersion = isItemRenderableByVersion;
|
|
44950
|
-
exports.
|
|
45251
|
+
exports.isWidgetIdInContent = isWidgetIdInContent;
|
|
44951
45252
|
exports.isWrongAnswerSupported = isWrongAnswerSupported;
|
|
44952
45253
|
exports.itemDataVersion = itemDataVersion;
|
|
44953
45254
|
exports.itemToTree = itemToTree;
|