@khanacademy/perseus 0.4.2 → 1.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/.eslintrc.js +11 -0
- package/CHANGELOG.md +47 -0
- package/dist/es/index.js +1078 -1299
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +1077 -1304
- package/dist/index.js.map +1 -1
- package/dist/strings.js +0 -4
- package/package.json +9 -8
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +110 -110
- package/src/perseus-api.jsx +10 -36
- package/src/perseus-types.js +0 -138
- package/src/styles/constants.js +0 -2
- package/src/styles/variables.less +0 -2
- package/src/types.js +18 -35
- package/src/util.js +0 -6
- package/src/version.js +1 -1
- package/src/widgets/__testdata__/base-radio_testdata.js +22 -0
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +110 -110
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +1136 -1160
- package/src/widgets/__tests__/radio/base-radio_test.jsx +243 -41
- package/src/widgets/__tests__/radio/choice-icon_test.jsx +67 -0
- package/src/widgets/__tests__/radio/choice_test.jsx +201 -0
- package/src/widgets/__tests__/radio/focus-ring_test.jsx +23 -0
- package/src/widgets/__tests__/radio/option-status_test.jsx +65 -0
- package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -9
- package/src/widgets/passage/get-line-height-for-node.js +4 -5
- package/src/widgets/passage/passage-markdown.jsx +176 -120
- package/src/widgets/passage.jsx +48 -46
- package/src/widgets/radio/__stories__/base-radio.stories.jsx +164 -17
- package/src/widgets/radio/__stories__/choice-icon.stories.jsx +202 -111
- package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +21 -6
- package/src/widgets/radio/__stories__/choice.stories.jsx +138 -0
- package/src/widgets/radio/__stories__/focus-ring.stories.jsx +29 -23
- package/src/widgets/radio/__stories__/option-status.stories.jsx +93 -99
- package/src/widgets/radio/base-radio.jsx +483 -556
- package/src/widgets/radio/choice-icon/choice-icon.jsx +155 -0
- package/src/widgets/radio/choice-icon/cross-out-line.jsx +55 -0
- package/src/widgets/radio/choice-icon/library-choice-icon.jsx +219 -0
- package/src/widgets/radio/choice-icon/sat-choice-icon.jsx +129 -0
- package/src/widgets/radio/choice-icon/shared-styles.js +17 -0
- package/src/widgets/radio/choice-none-above.jsx +49 -40
- package/src/widgets/radio/choice.jsx +282 -353
- package/src/widgets/radio/choice.jsx.fixture.js +0 -18
- package/src/widgets/radio/focus-ring.jsx +29 -22
- package/src/widgets/radio/option-status.jsx +58 -36
- package/src/widgets/radio/widget.jsx +181 -136
- package/src/widgets/radio.jsx +16 -17
- package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +0 -64
- package/src/widgets/radio/choice-icon.jsx +0 -469
- package/src/widgets/radio/cross-out-button.jsx +0 -134
package/dist/index.js
CHANGED
|
@@ -73,7 +73,7 @@ var Link__default = /*#__PURE__*/_interopDefaultLegacy(Link);
|
|
|
73
73
|
|
|
74
74
|
var version = {
|
|
75
75
|
apiVersion: {
|
|
76
|
-
major:
|
|
76
|
+
major: 12,
|
|
77
77
|
minor: 0
|
|
78
78
|
},
|
|
79
79
|
itemDataVersion: {
|
|
@@ -438,38 +438,9 @@ const ApiOptions = {
|
|
|
438
438
|
// values. For now, though, you must either specify all fields
|
|
439
439
|
// of `styling`, or omit the `styling` option entirely.
|
|
440
440
|
styling: PropTypes__default["default"].shape({
|
|
441
|
-
//
|
|
442
|
-
//
|
|
443
|
-
|
|
444
|
-
// "legacy" was the version of the widget display after XOM but
|
|
445
|
-
// before we started adding MCR styles. It doesn't have support
|
|
446
|
-
// for rationales. It has since been removed.
|
|
447
|
-
//
|
|
448
|
-
// "intermediate" is a design which adds several new additions
|
|
449
|
-
// to the "legacy" styles such as:
|
|
450
|
-
// 1. Using the XOM "desktop" styles (with a visible check icon
|
|
451
|
-
// and lines in between choices) on mobile devices.
|
|
452
|
-
// 2. Designs for rationales
|
|
453
|
-
// 3. Using the single-select styles for multi-select styles
|
|
454
|
-
//
|
|
455
|
-
// "final" is a design which will build off of the
|
|
456
|
-
// "intermediate" designs and adds some improved designs as well
|
|
457
|
-
// as:
|
|
458
|
-
// 1. a/b/c/d/etc. letters inside of the prompt check box
|
|
459
|
-
// 2. New iconography and styles to indicate choice correctness
|
|
460
|
-
//
|
|
461
|
-
// The "legacy" and "intermediate" designs will be A/B tested
|
|
462
|
-
// against each other to ensure that its changes don't cause
|
|
463
|
-
// problems due to the new designs. Once the "intermediate"
|
|
464
|
-
// designs are finished, they will be A/B tested against the
|
|
465
|
-
// "final" designs.
|
|
466
|
-
//
|
|
467
|
-
// If no flag is provided, "legacy" styles will be shown.
|
|
468
|
-
//
|
|
469
|
-
// TODO(emily): Remove this by Aug 1, 2017, at which point all
|
|
470
|
-
// callsites should have been switched to using the "final"
|
|
471
|
-
// designs.
|
|
472
|
-
radioStyleVersion: PropTypes__default["default"].oneOf(["intermediate", "final"])
|
|
441
|
+
// TODO(Nicole): Remove this after landing wonderblock-ifying
|
|
442
|
+
// the radio widget
|
|
443
|
+
primaryProductColor: PropTypes__default["default"].string
|
|
473
444
|
}),
|
|
474
445
|
// The color used for the hint progress indicator (eg. 1 / 3)
|
|
475
446
|
hintProgressColor: PropTypes__default["default"].string,
|
|
@@ -520,9 +491,7 @@ const ApiOptions = {
|
|
|
520
491
|
},
|
|
521
492
|
setDrawingAreaAvailable: function () {},
|
|
522
493
|
useDraftEditor: false,
|
|
523
|
-
styling: {
|
|
524
|
-
radioStyleVersion: "final"
|
|
525
|
-
},
|
|
494
|
+
styling: {},
|
|
526
495
|
canScrollPage: false,
|
|
527
496
|
inModal: false,
|
|
528
497
|
crossOutEnabled: false,
|
|
@@ -1643,16 +1612,10 @@ function inputPathsEqual(a, b) {
|
|
|
1643
1612
|
return a.length === b.length && a.every((item, index) => {
|
|
1644
1613
|
return b[index] === item;
|
|
1645
1614
|
});
|
|
1646
|
-
}
|
|
1647
|
-
// in content_internal/perseus_traversal.py
|
|
1648
|
-
// sync-start:widget-id-regex 1271666319 services/content-editing/perseus/translation/content_traversal.go
|
|
1649
|
-
// sync-start:widget-id-regex 680240484 services/static/javascript/perseus-merged-editor-package/editor.jsx
|
|
1650
|
-
// sync-start:widget-id-regex 1400368396 content_internal/perseus_traversal.py
|
|
1651
|
-
|
|
1615
|
+
}
|
|
1652
1616
|
|
|
1653
1617
|
const rWidgetRule = /^\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/;
|
|
1654
|
-
const rTypeFromWidgetId = /^([a-z-]+) ([0-9]+)$/;
|
|
1655
|
-
|
|
1618
|
+
const rTypeFromWidgetId = /^([a-z-]+) ([0-9]+)$/;
|
|
1656
1619
|
const rWidgetParts = new RegExp(rWidgetRule.source + "$");
|
|
1657
1620
|
const snowman = "\u2603";
|
|
1658
1621
|
const noScore = {
|
|
@@ -3085,11 +3048,10 @@ const warningColor = "#f86700";
|
|
|
3085
3048
|
const warningColorHover = "#df5c00";
|
|
3086
3049
|
const warningColorActive = "#c75300";
|
|
3087
3050
|
const publishBlockingErrorColor = "#be2612";
|
|
3088
|
-
const radioBorderColor$
|
|
3089
|
-
const checkedColor$1 = "#71B307";
|
|
3090
|
-
|
|
3051
|
+
const radioBorderColor$1 = "#BABEC2";
|
|
3052
|
+
const checkedColor$1 = "#71B307";
|
|
3091
3053
|
const articleMaxWidthInPx = 688;
|
|
3092
|
-
const articleMaxWidthTableInPx = 512;
|
|
3054
|
+
const articleMaxWidthTableInPx = 512;
|
|
3093
3055
|
|
|
3094
3056
|
var constants$1 = /*#__PURE__*/Object.freeze({
|
|
3095
3057
|
__proto__: null,
|
|
@@ -3154,7 +3116,7 @@ var constants$1 = /*#__PURE__*/Object.freeze({
|
|
|
3154
3116
|
warningColorHover: warningColorHover,
|
|
3155
3117
|
warningColorActive: warningColorActive,
|
|
3156
3118
|
publishBlockingErrorColor: publishBlockingErrorColor,
|
|
3157
|
-
radioBorderColor: radioBorderColor$
|
|
3119
|
+
radioBorderColor: radioBorderColor$1,
|
|
3158
3120
|
checkedColor: checkedColor$1,
|
|
3159
3121
|
articleMaxWidthInPx: articleMaxWidthInPx,
|
|
3160
3122
|
articleMaxWidthTableInPx: articleMaxWidthTableInPx
|
|
@@ -17888,17 +17850,17 @@ class Lint extends React__namespace.Component {
|
|
|
17888
17850
|
let warningTextStyle;
|
|
17889
17851
|
|
|
17890
17852
|
if (this.props.severity === 1) {
|
|
17891
|
-
severityStyle = styles$
|
|
17853
|
+
severityStyle = styles$A.indicatorError;
|
|
17892
17854
|
warningText = "Error";
|
|
17893
|
-
warningTextStyle = styles$
|
|
17855
|
+
warningTextStyle = styles$A.publishBlockingError;
|
|
17894
17856
|
} else if (this.props.severity === 2) {
|
|
17895
|
-
severityStyle = styles$
|
|
17857
|
+
severityStyle = styles$A.indicatorWarning;
|
|
17896
17858
|
warningText = "Warning";
|
|
17897
|
-
warningTextStyle = styles$
|
|
17859
|
+
warningTextStyle = styles$A.warning;
|
|
17898
17860
|
} else {
|
|
17899
|
-
severityStyle = styles$
|
|
17861
|
+
severityStyle = styles$A.indicatorGuideline;
|
|
17900
17862
|
warningText = "Recommendation";
|
|
17901
|
-
warningTextStyle = styles$
|
|
17863
|
+
warningTextStyle = styles$A.warning;
|
|
17902
17864
|
}
|
|
17903
17865
|
|
|
17904
17866
|
return /*#__PURE__*/React__namespace.createElement("a", {
|
|
@@ -17906,16 +17868,16 @@ class Lint extends React__namespace.Component {
|
|
|
17906
17868
|
target: "lint-help-window",
|
|
17907
17869
|
className: aphrodite.css(style)
|
|
17908
17870
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
17909
|
-
className: aphrodite.css(styles$
|
|
17871
|
+
className: aphrodite.css(styles$A.indicator, severityStyle)
|
|
17910
17872
|
}, this.props.severity === 1 && /*#__PURE__*/React__namespace.createElement(InlineIcon, exclamationIcon)), /*#__PURE__*/React__namespace.createElement("div", {
|
|
17911
|
-
className: aphrodite.css(styles$
|
|
17873
|
+
className: aphrodite.css(styles$A.tooltip, tooltipAbove && styles$A.tooltipAbove)
|
|
17912
17874
|
}, this.props.message.split("\n\n").map((m, i) => /*#__PURE__*/React__namespace.createElement("p", {
|
|
17913
17875
|
key: i,
|
|
17914
|
-
className: aphrodite.css(styles$
|
|
17876
|
+
className: aphrodite.css(styles$A.tooltipParagraph)
|
|
17915
17877
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
17916
17878
|
className: aphrodite.css(warningTextStyle)
|
|
17917
17879
|
}, warningText, ":", " "), m)), /*#__PURE__*/React__namespace.createElement("div", {
|
|
17918
|
-
className: aphrodite.css(styles$
|
|
17880
|
+
className: aphrodite.css(styles$A.tail, tooltipAbove && styles$A.tailAbove)
|
|
17919
17881
|
})));
|
|
17920
17882
|
});
|
|
17921
17883
|
}
|
|
@@ -17963,19 +17925,19 @@ class Lint extends React__namespace.Component {
|
|
|
17963
17925
|
|
|
17964
17926
|
if (blockHighlight) {
|
|
17965
17927
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
17966
|
-
className: aphrodite.css(styles$
|
|
17967
|
-
}, this.renderLink(styles$
|
|
17928
|
+
className: aphrodite.css(styles$A.lintContainer, styles$A.lintContainerBlock)
|
|
17929
|
+
}, this.renderLink(styles$A.radioWidgetHoverTarget), /*#__PURE__*/React__namespace.createElement("span", null, children));
|
|
17968
17930
|
}
|
|
17969
17931
|
|
|
17970
17932
|
if (inline) {
|
|
17971
17933
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
17972
|
-
className: aphrodite.css(styles$
|
|
17973
|
-
}, this.renderLink(styles$
|
|
17934
|
+
className: aphrodite.css(styles$A.lintContainer)
|
|
17935
|
+
}, this.renderLink(styles$A.inlineHoverTarget), /*#__PURE__*/React__namespace.createElement("span", null, children));
|
|
17974
17936
|
}
|
|
17975
17937
|
|
|
17976
17938
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
17977
|
-
className: aphrodite.css(styles$
|
|
17978
|
-
}, this.renderLink(styles$
|
|
17939
|
+
className: aphrodite.css(styles$A.lintContainer)
|
|
17940
|
+
}, this.renderLink(styles$A.hoverTarget), /*#__PURE__*/React__namespace.createElement("div", null, children));
|
|
17979
17941
|
}
|
|
17980
17942
|
|
|
17981
17943
|
} // $FlowFixMe[prop-missing]
|
|
@@ -18000,7 +17962,7 @@ _defineProperty(Lint, "propTypes", {
|
|
|
18000
17962
|
severity: PropTypes__default["default"].number
|
|
18001
17963
|
});
|
|
18002
17964
|
|
|
18003
|
-
const styles$
|
|
17965
|
+
const styles$A = aphrodite.StyleSheet.create({
|
|
18004
17966
|
// This is the class of the outermost element.
|
|
18005
17967
|
// We use relative positioning so that the lint indicator can be
|
|
18006
17968
|
// positioned absolutely relative to the position of the linty container.
|
|
@@ -19893,7 +19855,7 @@ class Renderer extends React__namespace.Component {
|
|
|
19893
19855
|
paddingRight: margin
|
|
19894
19856
|
};
|
|
19895
19857
|
const mobileInnerStyle = { ...innerStyle,
|
|
19896
|
-
...styles$
|
|
19858
|
+
...styles$z.mobileZoomableParentFix
|
|
19897
19859
|
};
|
|
19898
19860
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
19899
19861
|
key: state.key,
|
|
@@ -20033,7 +19995,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20033
19995
|
paddingRight: 0
|
|
20034
19996
|
};
|
|
20035
19997
|
const mobileInnerStyle = { ...innerStyle,
|
|
20036
|
-
...styles$
|
|
19998
|
+
...styles$z.mobileZoomableParentFix
|
|
20037
19999
|
};
|
|
20038
20000
|
const wrappedOutput = /*#__PURE__*/React__namespace.createElement("div", {
|
|
20039
20001
|
style: { ...mobileInnerStyle,
|
|
@@ -20714,7 +20676,7 @@ _defineProperty(Renderer, "defaultProps", {
|
|
|
20714
20676
|
linterContext: PerseusLinter__namespace.linterContextDefault
|
|
20715
20677
|
});
|
|
20716
20678
|
|
|
20717
|
-
const styles$
|
|
20679
|
+
const styles$z = {
|
|
20718
20680
|
mobileZoomableParentFix: {
|
|
20719
20681
|
// NOTE(abdul): There is an issue where transform animations will
|
|
20720
20682
|
// cause the Zoomable component to disappear when running on the
|
|
@@ -24016,7 +23978,7 @@ var mediaQueries = {
|
|
|
24016
23978
|
const {
|
|
24017
23979
|
zIndexAboveScratchpad,
|
|
24018
23980
|
zIndexInteractiveComponent,
|
|
24019
|
-
radioBorderColor
|
|
23981
|
+
radioBorderColor,
|
|
24020
23982
|
checkedColor,
|
|
24021
23983
|
circleSize,
|
|
24022
23984
|
radioMarginWidth
|
|
@@ -24071,7 +24033,7 @@ var sharedStyles = aphrodite.StyleSheet.create({
|
|
|
24071
24033
|
},
|
|
24072
24034
|
backgroundColor: "#fff",
|
|
24073
24035
|
border: "2px solid #fff",
|
|
24074
|
-
boxShadow: "0 0px 0px 1px ".concat(radioBorderColor
|
|
24036
|
+
boxShadow: "0 0px 0px 1px ".concat(radioBorderColor),
|
|
24075
24037
|
outline: "none",
|
|
24076
24038
|
boxSizing: "border-box",
|
|
24077
24039
|
flexShrink: 0,
|
|
@@ -24323,36 +24285,6 @@ _defineProperty(Icon, "defaultProps", {
|
|
|
24323
24285
|
color: "currentColor"
|
|
24324
24286
|
});
|
|
24325
24287
|
|
|
24326
|
-
class FocusRing extends React__namespace.Component {
|
|
24327
|
-
render() {
|
|
24328
|
-
const borderColor = this.props.visible ? this.props.color : "transparent";
|
|
24329
|
-
const style = {
|
|
24330
|
-
borderColor: borderColor
|
|
24331
|
-
};
|
|
24332
|
-
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
24333
|
-
className: aphrodite.css(styles$u.ring),
|
|
24334
|
-
style: style
|
|
24335
|
-
}, this.props.children);
|
|
24336
|
-
}
|
|
24337
|
-
|
|
24338
|
-
}
|
|
24339
|
-
|
|
24340
|
-
_defineProperty(FocusRing, "defaultProps", {
|
|
24341
|
-
visible: true,
|
|
24342
|
-
color: kaGreen$1
|
|
24343
|
-
});
|
|
24344
|
-
|
|
24345
|
-
const styles$u = aphrodite.StyleSheet.create({
|
|
24346
|
-
ring: {
|
|
24347
|
-
margin: "auto",
|
|
24348
|
-
display: "inline-block",
|
|
24349
|
-
borderRadius: "50%",
|
|
24350
|
-
borderWidth: 2,
|
|
24351
|
-
padding: 2,
|
|
24352
|
-
borderStyle: "solid"
|
|
24353
|
-
}
|
|
24354
|
-
});
|
|
24355
|
-
|
|
24356
24288
|
/**
|
|
24357
24289
|
* Given a choice's position in the radio widget, return the corresponding
|
|
24358
24290
|
* "choice letter". (For example, in English, `getChoiceLetter(0)` is "A",
|
|
@@ -24383,179 +24315,57 @@ function getChoiceLetter(pos) {
|
|
|
24383
24315
|
return " ";
|
|
24384
24316
|
}
|
|
24385
24317
|
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
// TODO(amy): figure out a better scheme for specifying these
|
|
24390
|
-
// styles that isn't such a pain to grok. See some neat ideas
|
|
24391
|
-
// from MDR in https://phabricator.khanacademy.org/D35249.
|
|
24392
|
-
constructStyles(reviewMode, correct, checked) {
|
|
24393
|
-
let backgroundColor;
|
|
24394
|
-
let borderColor = satBlue;
|
|
24395
|
-
let color = satBlue;
|
|
24396
|
-
|
|
24397
|
-
if (reviewMode) {
|
|
24398
|
-
if (correct) {
|
|
24399
|
-
borderColor = satCorrectColor;
|
|
24400
|
-
color = checked ? white$1 : satCorrectColor;
|
|
24401
|
-
backgroundColor = checked ? satCorrectColor : white$1;
|
|
24402
|
-
} else if (checked) {
|
|
24403
|
-
borderColor = satIncorrectColor;
|
|
24404
|
-
color = white$1;
|
|
24405
|
-
backgroundColor = satIncorrectColor;
|
|
24406
|
-
}
|
|
24407
|
-
} else if (checked) {
|
|
24408
|
-
color = white$1;
|
|
24409
|
-
backgroundColor = satBlue;
|
|
24410
|
-
}
|
|
24411
|
-
|
|
24412
|
-
return {
|
|
24413
|
-
color,
|
|
24414
|
-
backgroundColor,
|
|
24415
|
-
borderColor
|
|
24416
|
-
};
|
|
24417
|
-
}
|
|
24418
|
-
|
|
24419
|
-
render() {
|
|
24420
|
-
const {
|
|
24421
|
-
letter,
|
|
24422
|
-
a11yText,
|
|
24423
|
-
reviewMode,
|
|
24424
|
-
checked,
|
|
24425
|
-
correct,
|
|
24426
|
-
crossedOut
|
|
24427
|
-
} = this.props;
|
|
24428
|
-
const {
|
|
24429
|
-
color,
|
|
24430
|
-
backgroundColor,
|
|
24431
|
-
borderColor
|
|
24432
|
-
} = this.constructStyles(reviewMode, correct, checked);
|
|
24433
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24434
|
-
className: aphrodite.css(styles$t.iconWrapper)
|
|
24435
|
-
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
24436
|
-
className: aphrodite.css(styles$t.satCircle),
|
|
24437
|
-
style: {
|
|
24438
|
-
backgroundColor,
|
|
24439
|
-
borderColor
|
|
24440
|
-
}
|
|
24441
|
-
}), /*#__PURE__*/React__namespace.createElement("div", {
|
|
24442
|
-
style: {
|
|
24443
|
-
color
|
|
24444
|
-
},
|
|
24445
|
-
className: aphrodite.css(styles$t.letter)
|
|
24446
|
-
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
24447
|
-
className: "perseus-sr-only"
|
|
24448
|
-
}, a11yText), /*#__PURE__*/React__namespace.createElement("span", {
|
|
24449
|
-
"aria-hidden": "true"
|
|
24450
|
-
}, letter)), crossedOut && /*#__PURE__*/React__namespace.createElement(CrossOutLine, {
|
|
24451
|
-
color: borderColor,
|
|
24452
|
-
sat: true
|
|
24453
|
-
}));
|
|
24454
|
-
}
|
|
24318
|
+
/**
|
|
24319
|
+
* Renders a circular selection ring around the child.
|
|
24320
|
+
*/
|
|
24455
24321
|
|
|
24322
|
+
function FocusRing(props) {
|
|
24323
|
+
const {
|
|
24324
|
+
visible,
|
|
24325
|
+
color,
|
|
24326
|
+
children,
|
|
24327
|
+
multipleSelect
|
|
24328
|
+
} = props;
|
|
24329
|
+
const borderColor = visible ? color : "transparent";
|
|
24330
|
+
const borderRadius = multipleSelect ? 5 : "50%";
|
|
24331
|
+
const style = {
|
|
24332
|
+
borderColor,
|
|
24333
|
+
borderRadius
|
|
24334
|
+
};
|
|
24335
|
+
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
24336
|
+
"data-test-id": "focus-ring",
|
|
24337
|
+
className: aphrodite.css(styles$y.ring),
|
|
24338
|
+
style: style
|
|
24339
|
+
}, children);
|
|
24456
24340
|
}
|
|
24457
24341
|
|
|
24458
|
-
|
|
24459
|
-
|
|
24460
|
-
|
|
24461
|
-
|
|
24462
|
-
|
|
24463
|
-
|
|
24464
|
-
|
|
24465
|
-
|
|
24466
|
-
|
|
24467
|
-
|
|
24468
|
-
|
|
24469
|
-
|
|
24470
|
-
}
|
|
24471
|
-
|
|
24472
|
-
if (correct) {
|
|
24473
|
-
return /*#__PURE__*/React__namespace.createElement(InlineIcon, _extends({}, iconCheck, {
|
|
24474
|
-
style: {
|
|
24475
|
-
position: "relative",
|
|
24476
|
-
top: -1
|
|
24477
|
-
}
|
|
24478
|
-
}));
|
|
24479
|
-
}
|
|
24480
|
-
|
|
24481
|
-
return /*#__PURE__*/React__namespace.createElement(InlineIcon, iconMinus);
|
|
24482
|
-
} // Handle dynamic styling of the multiple choice icon. Most
|
|
24483
|
-
// MC icon styles are constant, but we do allow the caller
|
|
24484
|
-
// to specify the selected color, and thus must control styles
|
|
24485
|
-
// related to the selected state dynamically.
|
|
24486
|
-
|
|
24487
|
-
|
|
24488
|
-
getDynamicStyles() {
|
|
24489
|
-
const {
|
|
24490
|
-
checked,
|
|
24491
|
-
showCorrectness,
|
|
24492
|
-
pressed,
|
|
24493
|
-
primaryProductColor,
|
|
24494
|
-
correct,
|
|
24495
|
-
transparentBackground
|
|
24496
|
-
} = this.props;
|
|
24497
|
-
|
|
24498
|
-
if (!showCorrectness && pressed) {
|
|
24499
|
-
return {
|
|
24500
|
-
borderColor: primaryProductColor,
|
|
24501
|
-
color: primaryProductColor,
|
|
24502
|
-
backgroundColor: transparentBackground ? "transparent" : white$1
|
|
24503
|
-
};
|
|
24504
|
-
}
|
|
24505
|
-
|
|
24506
|
-
if (checked) {
|
|
24507
|
-
// Note: kaGreen is not only the default product color,
|
|
24508
|
-
// but also the "correctness" color
|
|
24509
|
-
const bg = showCorrectness && correct ? Color__default["default"].green : primaryProductColor;
|
|
24510
|
-
return {
|
|
24511
|
-
color: white$1,
|
|
24512
|
-
backgroundColor: bg,
|
|
24513
|
-
borderColor: bg
|
|
24514
|
-
};
|
|
24515
|
-
}
|
|
24516
|
-
|
|
24517
|
-
return {
|
|
24518
|
-
borderColor: Color__default["default"].offBlack64,
|
|
24519
|
-
color: Color__default["default"].offBlack64
|
|
24520
|
-
};
|
|
24342
|
+
FocusRing.defaultProps = {
|
|
24343
|
+
visible: true,
|
|
24344
|
+
color: kaGreen$1,
|
|
24345
|
+
multipleSelect: false
|
|
24346
|
+
};
|
|
24347
|
+
const styles$y = aphrodite.StyleSheet.create({
|
|
24348
|
+
ring: {
|
|
24349
|
+
margin: "auto",
|
|
24350
|
+
display: "inline-block",
|
|
24351
|
+
borderWidth: 2,
|
|
24352
|
+
padding: 2,
|
|
24353
|
+
borderStyle: "solid"
|
|
24521
24354
|
}
|
|
24355
|
+
});
|
|
24522
24356
|
|
|
24523
|
-
|
|
24524
|
-
|
|
24525
|
-
|
|
24526
|
-
|
|
24527
|
-
|
|
24528
|
-
|
|
24529
|
-
correct,
|
|
24530
|
-
focused,
|
|
24531
|
-
primaryProductColor,
|
|
24532
|
-
previouslyAnswered
|
|
24533
|
-
} = this.props;
|
|
24534
|
-
const dynamicStyles = this.getDynamicStyles();
|
|
24535
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24536
|
-
className: aphrodite.css(styles$t.iconWrapper)
|
|
24537
|
-
}, /*#__PURE__*/React__namespace.createElement(FocusRing, {
|
|
24538
|
-
color: primaryProductColor,
|
|
24539
|
-
visible: focused
|
|
24540
|
-
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
24541
|
-
style: dynamicStyles,
|
|
24542
|
-
className: aphrodite.css(styles$t.libraryCircle, showCorrectness && correct && styles$t.libraryCircleCorrect, showCorrectness && !correct && styles$t.libraryCircleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$t.libraryCircleIncorrectAnswered) // used in BaseRadio to check if we actually clicked on the
|
|
24543
|
-
// radio icon
|
|
24544
|
-
,
|
|
24545
|
-
"data-is-radio-icon": true
|
|
24546
|
-
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
24547
|
-
className: "perseus-sr-only"
|
|
24548
|
-
}, a11yText), /*#__PURE__*/React__namespace.createElement("div", {
|
|
24549
|
-
"aria-hidden": true
|
|
24550
|
-
}, this.getChoiceInner()))), crossedOut && /*#__PURE__*/React__namespace.createElement(CrossOutLine, {
|
|
24551
|
-
color: dynamicStyles.borderColor
|
|
24552
|
-
}));
|
|
24357
|
+
const SAT_ICON_SIZE = 25;
|
|
24358
|
+
const LIBRARY_ICON_SIZE = 24;
|
|
24359
|
+
const styles$x = aphrodite.StyleSheet.create({
|
|
24360
|
+
iconWrapper: {
|
|
24361
|
+
display: "inline-block",
|
|
24362
|
+
position: "relative"
|
|
24553
24363
|
}
|
|
24364
|
+
});
|
|
24554
24365
|
|
|
24555
|
-
|
|
24366
|
+
/* Component for rendering a letter icon in radio choice */
|
|
24556
24367
|
// extra space for the cute rounded line cap.
|
|
24557
24368
|
|
|
24558
|
-
|
|
24559
24369
|
const CROSS_OUT_LINE_PADDING = 2;
|
|
24560
24370
|
const CROSS_OUT_LINE_SIZE = LIBRARY_ICON_SIZE + CROSS_OUT_LINE_PADDING * 2;
|
|
24561
24371
|
const SAT_CROSS_OUT_LINE_SIZE = SAT_ICON_SIZE + CROSS_OUT_LINE_PADDING * 2;
|
|
@@ -24570,7 +24380,7 @@ function CrossOutLine(props) {
|
|
|
24570
24380
|
width: crossOutLineSize,
|
|
24571
24381
|
height: crossOutLineSize,
|
|
24572
24382
|
viewBox: "0 0 ".concat(crossOutLineSize, " ").concat(crossOutLineSize),
|
|
24573
|
-
className: aphrodite.css(styles$
|
|
24383
|
+
className: aphrodite.css(styles$w.crossOutLine)
|
|
24574
24384
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
24575
24385
|
// The line stretches from the bottom-left to top-right.
|
|
24576
24386
|
// We don't quite go to the _very_ corner, because the cute
|
|
@@ -24585,151 +24395,129 @@ function CrossOutLine(props) {
|
|
|
24585
24395
|
}));
|
|
24586
24396
|
}
|
|
24587
24397
|
|
|
24588
|
-
|
|
24589
|
-
|
|
24590
|
-
|
|
24591
|
-
|
|
24592
|
-
|
|
24593
|
-
|
|
24594
|
-
|
|
24595
|
-
|
|
24596
|
-
// whether the answer was checked/crossed-out/neither, and whether the
|
|
24597
|
-
// answer is correct/incorrect/not-yet-revealed.
|
|
24598
|
-
//
|
|
24599
|
-
// Translation is tricky for cross-product situations like this, so
|
|
24600
|
-
// we've just enumerated all 9 possibilities as separate strings.
|
|
24601
|
-
|
|
24602
|
-
if (showCorrectness && correct) {
|
|
24603
|
-
if (checked) {
|
|
24604
|
-
return i18n__namespace._("(Choice %(letter)s, Checked, Correct)", {
|
|
24605
|
-
letter
|
|
24606
|
-
});
|
|
24607
|
-
}
|
|
24608
|
-
|
|
24609
|
-
if (crossedOut) {
|
|
24610
|
-
return i18n__namespace._("(Choice %(letter)s, Crossed out, Correct)", {
|
|
24611
|
-
letter
|
|
24612
|
-
});
|
|
24613
|
-
}
|
|
24398
|
+
const styles$w = aphrodite.StyleSheet.create({
|
|
24399
|
+
crossOutLine: {
|
|
24400
|
+
// Center the icon within the container.
|
|
24401
|
+
position: "absolute",
|
|
24402
|
+
top: "calc(50% - ".concat(CROSS_OUT_LINE_SIZE / 2, "px)"),
|
|
24403
|
+
left: "calc(50% - ".concat(CROSS_OUT_LINE_SIZE / 2, "px)")
|
|
24404
|
+
}
|
|
24405
|
+
});
|
|
24614
24406
|
|
|
24615
|
-
|
|
24616
|
-
|
|
24617
|
-
|
|
24618
|
-
|
|
24407
|
+
function ChoiceInner(props) {
|
|
24408
|
+
const {
|
|
24409
|
+
letter,
|
|
24410
|
+
showCorrectness,
|
|
24411
|
+
correct
|
|
24412
|
+
} = props;
|
|
24619
24413
|
|
|
24620
|
-
|
|
24621
|
-
|
|
24622
|
-
|
|
24623
|
-
letter
|
|
24624
|
-
});
|
|
24625
|
-
}
|
|
24414
|
+
if (!showCorrectness) {
|
|
24415
|
+
return letter;
|
|
24416
|
+
}
|
|
24626
24417
|
|
|
24627
|
-
|
|
24628
|
-
|
|
24629
|
-
|
|
24630
|
-
|
|
24418
|
+
if (correct) {
|
|
24419
|
+
return /*#__PURE__*/React__namespace.createElement(InlineIcon, _extends({}, iconCheck, {
|
|
24420
|
+
style: {
|
|
24421
|
+
position: "relative",
|
|
24422
|
+
top: -1
|
|
24631
24423
|
}
|
|
24632
|
-
|
|
24633
|
-
return i18n__namespace._("(Choice %(letter)s, Incorrect)", {
|
|
24634
|
-
letter
|
|
24635
|
-
});
|
|
24636
|
-
}
|
|
24637
|
-
|
|
24638
|
-
if (checked) {
|
|
24639
|
-
return i18n__namespace._("(Choice %(letter)s, Checked)", {
|
|
24640
|
-
letter
|
|
24641
|
-
});
|
|
24642
|
-
}
|
|
24643
|
-
|
|
24644
|
-
if (crossedOut) {
|
|
24645
|
-
return i18n__namespace._("(Choice %(letter)s, Crossed out)", {
|
|
24646
|
-
letter
|
|
24647
|
-
});
|
|
24648
|
-
}
|
|
24649
|
-
|
|
24650
|
-
return i18n__namespace._("(Choice %(letter)s)", {
|
|
24651
|
-
letter
|
|
24652
|
-
});
|
|
24424
|
+
}));
|
|
24653
24425
|
}
|
|
24654
24426
|
|
|
24655
|
-
|
|
24656
|
-
|
|
24657
|
-
|
|
24658
|
-
|
|
24659
|
-
|
|
24660
|
-
|
|
24661
|
-
|
|
24662
|
-
|
|
24663
|
-
|
|
24664
|
-
|
|
24665
|
-
|
|
24666
|
-
|
|
24667
|
-
|
|
24668
|
-
|
|
24669
|
-
|
|
24670
|
-
|
|
24427
|
+
return /*#__PURE__*/React__namespace.createElement(InlineIcon, iconMinus);
|
|
24428
|
+
} // Handle dynamic styling of the multiple choice icon. Most
|
|
24429
|
+
// MC icon styles are constant, but we do allow the caller
|
|
24430
|
+
// to specify the selected color, and thus must control styles
|
|
24431
|
+
// related to the selected state dynamically.
|
|
24432
|
+
|
|
24433
|
+
|
|
24434
|
+
function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, primaryProductColor, correct, transparentBackground) {
|
|
24435
|
+
let backgroundColor;
|
|
24436
|
+
let borderColor;
|
|
24437
|
+
let color;
|
|
24438
|
+
|
|
24439
|
+
if (!showCorrectness && pressed) {
|
|
24440
|
+
borderColor = primaryProductColor;
|
|
24441
|
+
color = primaryProductColor;
|
|
24442
|
+
backgroundColor = transparentBackground ? "transparent" : white$1;
|
|
24443
|
+
} else if (checked) {
|
|
24444
|
+
// Note: kaGreen is not only the default product color,
|
|
24445
|
+
// but also the "correctness" color
|
|
24446
|
+
const bg = showCorrectness && correct ? Color__default["default"].green : primaryProductColor;
|
|
24447
|
+
color = white$1;
|
|
24448
|
+
backgroundColor = bg;
|
|
24449
|
+
borderColor = bg;
|
|
24450
|
+
} else {
|
|
24451
|
+
borderColor = Color__default["default"].offBlack64;
|
|
24452
|
+
color = Color__default["default"].offBlack64;
|
|
24453
|
+
} // define shape
|
|
24671
24454
|
|
|
24672
|
-
if (product === "sat") {
|
|
24673
|
-
return /*#__PURE__*/React__namespace.createElement(SATChoiceIcon, {
|
|
24674
|
-
letter: letter,
|
|
24675
|
-
a11yText: this.a11yText(letter),
|
|
24676
|
-
reviewMode: reviewMode,
|
|
24677
|
-
checked: checked,
|
|
24678
|
-
correct: correct,
|
|
24679
|
-
crossedOut: crossedOut
|
|
24680
|
-
});
|
|
24681
|
-
}
|
|
24682
24455
|
|
|
24683
|
-
|
|
24684
|
-
|
|
24685
|
-
|
|
24686
|
-
|
|
24687
|
-
|
|
24688
|
-
|
|
24689
|
-
pressed: pressed,
|
|
24690
|
-
focused: focused,
|
|
24691
|
-
correct: correct,
|
|
24692
|
-
showCorrectness: showCorrectness,
|
|
24693
|
-
primaryProductColor: primaryProductColor,
|
|
24694
|
-
previouslyAnswered: previouslyAnswered,
|
|
24695
|
-
transparentBackground: transparentBackground
|
|
24696
|
-
});
|
|
24456
|
+
let borderRadius;
|
|
24457
|
+
|
|
24458
|
+
if (multipleSelect) {
|
|
24459
|
+
borderRadius = 3;
|
|
24460
|
+
} else {
|
|
24461
|
+
borderRadius = LIBRARY_ICON_SIZE;
|
|
24697
24462
|
}
|
|
24698
24463
|
|
|
24464
|
+
return {
|
|
24465
|
+
backgroundColor,
|
|
24466
|
+
borderColor,
|
|
24467
|
+
color,
|
|
24468
|
+
borderRadius
|
|
24469
|
+
};
|
|
24699
24470
|
}
|
|
24700
24471
|
|
|
24701
|
-
|
|
24702
|
-
|
|
24703
|
-
|
|
24472
|
+
function LibraryChoiceIcon(props) {
|
|
24473
|
+
const {
|
|
24474
|
+
a11yText,
|
|
24475
|
+
checked,
|
|
24476
|
+
crossedOut,
|
|
24477
|
+
showCorrectness,
|
|
24478
|
+
correct,
|
|
24479
|
+
focused,
|
|
24480
|
+
hovered,
|
|
24481
|
+
multipleSelect,
|
|
24482
|
+
primaryProductColor,
|
|
24483
|
+
previouslyAnswered,
|
|
24484
|
+
letter,
|
|
24485
|
+
pressed,
|
|
24486
|
+
transparentBackground
|
|
24487
|
+
} = props;
|
|
24488
|
+
const dynamicStyles = getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, primaryProductColor, correct, transparentBackground);
|
|
24489
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24490
|
+
className: aphrodite.css(styles$x.iconWrapper)
|
|
24491
|
+
}, /*#__PURE__*/React__namespace.createElement(FocusRing, {
|
|
24492
|
+
color: primaryProductColor,
|
|
24493
|
+
visible: focused || hovered,
|
|
24494
|
+
multipleSelect: multipleSelect
|
|
24495
|
+
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
24496
|
+
style: dynamicStyles,
|
|
24497
|
+
"data-test-id": "choice-icon__library-choice-icon",
|
|
24498
|
+
className: aphrodite.css(styles$v.circle, showCorrectness && correct && styles$v.circleCorrect, showCorrectness && !correct && styles$v.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$v.circleIncorrectAnswered) // used in BaseRadio to check if we actually clicked on the
|
|
24499
|
+
// radio icon
|
|
24500
|
+
,
|
|
24501
|
+
"data-is-radio-icon": true
|
|
24502
|
+
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
24503
|
+
className: "perseus-sr-only"
|
|
24504
|
+
}, a11yText), /*#__PURE__*/React__namespace.createElement("div", {
|
|
24505
|
+
"aria-hidden": true
|
|
24506
|
+
}, /*#__PURE__*/React__namespace.createElement(ChoiceInner, {
|
|
24507
|
+
letter: letter,
|
|
24508
|
+
showCorrectness: showCorrectness,
|
|
24509
|
+
correct: correct
|
|
24510
|
+
})))), crossedOut && /*#__PURE__*/React__namespace.createElement(CrossOutLine, {
|
|
24511
|
+
color: dynamicStyles.borderColor
|
|
24512
|
+
}));
|
|
24513
|
+
}
|
|
24704
24514
|
|
|
24705
|
-
const styles$
|
|
24706
|
-
|
|
24707
|
-
display: "block",
|
|
24708
|
-
borderRadius: SAT_ICON_SIZE,
|
|
24709
|
-
borderStyle: "solid",
|
|
24710
|
-
borderWidth: 2,
|
|
24711
|
-
content: "''",
|
|
24712
|
-
height: SAT_ICON_SIZE,
|
|
24713
|
-
width: SAT_ICON_SIZE,
|
|
24714
|
-
top: 1,
|
|
24715
|
-
left: 1
|
|
24716
|
-
},
|
|
24717
|
-
iconWrapper: {
|
|
24718
|
-
display: "inline-block",
|
|
24719
|
-
position: "relative"
|
|
24720
|
-
},
|
|
24721
|
-
crossOutLine: {
|
|
24722
|
-
// Center the icon within the container.
|
|
24723
|
-
position: "absolute",
|
|
24724
|
-
top: "calc(50% - ".concat(CROSS_OUT_LINE_SIZE / 2, "px)"),
|
|
24725
|
-
left: "calc(50% - ".concat(CROSS_OUT_LINE_SIZE / 2, "px)")
|
|
24726
|
-
},
|
|
24727
|
-
libraryCircle: {
|
|
24515
|
+
const styles$v = aphrodite.StyleSheet.create({
|
|
24516
|
+
circle: {
|
|
24728
24517
|
// Make the circle
|
|
24729
24518
|
width: LIBRARY_ICON_SIZE,
|
|
24730
24519
|
height: LIBRARY_ICON_SIZE,
|
|
24731
24520
|
boxSizing: "border-box",
|
|
24732
|
-
borderRadius: LIBRARY_ICON_SIZE,
|
|
24733
24521
|
borderStyle: "solid",
|
|
24734
24522
|
borderWidth: 2,
|
|
24735
24523
|
// The default icons have letters in them. Style those letters.
|
|
@@ -24750,18 +24538,113 @@ const styles$t = aphrodite.StyleSheet.create({
|
|
|
24750
24538
|
// pixel so it is much better centered.
|
|
24751
24539
|
lineHeight: "1px"
|
|
24752
24540
|
},
|
|
24753
|
-
|
|
24541
|
+
circleCorrect: {
|
|
24754
24542
|
fontSize: LIBRARY_ICON_SIZE
|
|
24755
24543
|
},
|
|
24756
|
-
|
|
24544
|
+
circleIncorrect: {
|
|
24757
24545
|
fontSize: LIBRARY_ICON_SIZE,
|
|
24758
24546
|
borderColor: gray68,
|
|
24759
24547
|
color: gray68
|
|
24760
24548
|
},
|
|
24761
|
-
|
|
24549
|
+
circleIncorrectAnswered: {
|
|
24762
24550
|
backgroundColor: Color__default["default"].red,
|
|
24763
24551
|
borderColor: Color__default["default"].red,
|
|
24764
24552
|
color: Color__default["default"].white
|
|
24553
|
+
}
|
|
24554
|
+
});
|
|
24555
|
+
|
|
24556
|
+
/* Component for rendering a letter icon in a SAT radio choice */
|
|
24557
|
+
|
|
24558
|
+
// TODO(amy): figure out a better scheme for specifying these
|
|
24559
|
+
// styles that isn't such a pain to grok. See some neat ideas
|
|
24560
|
+
// from MDR in https://phabricator.khanacademy.org/D35249.
|
|
24561
|
+
function constructSATStyles(reviewMode, correct, checked, multipleSelect) {
|
|
24562
|
+
let backgroundColor;
|
|
24563
|
+
let borderColor = satBlue;
|
|
24564
|
+
let color = satBlue;
|
|
24565
|
+
|
|
24566
|
+
if (reviewMode) {
|
|
24567
|
+
if (correct) {
|
|
24568
|
+
borderColor = satCorrectColor;
|
|
24569
|
+
color = checked ? white$1 : satCorrectColor;
|
|
24570
|
+
backgroundColor = checked ? satCorrectColor : white$1;
|
|
24571
|
+
} else if (checked) {
|
|
24572
|
+
borderColor = satIncorrectColor;
|
|
24573
|
+
color = white$1;
|
|
24574
|
+
backgroundColor = satIncorrectColor;
|
|
24575
|
+
}
|
|
24576
|
+
} else if (checked) {
|
|
24577
|
+
color = white$1;
|
|
24578
|
+
backgroundColor = satBlue;
|
|
24579
|
+
}
|
|
24580
|
+
|
|
24581
|
+
let borderRadius;
|
|
24582
|
+
|
|
24583
|
+
if (multipleSelect) {
|
|
24584
|
+
borderRadius = 3;
|
|
24585
|
+
} else {
|
|
24586
|
+
borderRadius = SAT_ICON_SIZE;
|
|
24587
|
+
}
|
|
24588
|
+
|
|
24589
|
+
return {
|
|
24590
|
+
color,
|
|
24591
|
+
backgroundColor,
|
|
24592
|
+
borderColor,
|
|
24593
|
+
borderRadius
|
|
24594
|
+
};
|
|
24595
|
+
}
|
|
24596
|
+
|
|
24597
|
+
function SATChoiceIcon(props) {
|
|
24598
|
+
const {
|
|
24599
|
+
letter,
|
|
24600
|
+
a11yText,
|
|
24601
|
+
crossedOut,
|
|
24602
|
+
reviewMode,
|
|
24603
|
+
correct,
|
|
24604
|
+
checked,
|
|
24605
|
+
multipleSelect
|
|
24606
|
+
} = props;
|
|
24607
|
+
const {
|
|
24608
|
+
color,
|
|
24609
|
+
backgroundColor,
|
|
24610
|
+
borderColor,
|
|
24611
|
+
borderRadius
|
|
24612
|
+
} = constructSATStyles(reviewMode, correct, checked, multipleSelect);
|
|
24613
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24614
|
+
className: aphrodite.css(styles$x.iconWrapper)
|
|
24615
|
+
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
24616
|
+
className: aphrodite.css(styles$u.satCircle),
|
|
24617
|
+
"data-test-id": "choice-icon__sat-choice-icon",
|
|
24618
|
+
style: {
|
|
24619
|
+
backgroundColor,
|
|
24620
|
+
borderColor,
|
|
24621
|
+
borderRadius
|
|
24622
|
+
}
|
|
24623
|
+
}), /*#__PURE__*/React__namespace.createElement("div", {
|
|
24624
|
+
style: {
|
|
24625
|
+
color
|
|
24626
|
+
},
|
|
24627
|
+
className: aphrodite.css(styles$u.letter)
|
|
24628
|
+
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
24629
|
+
className: "perseus-sr-only"
|
|
24630
|
+
}, a11yText), /*#__PURE__*/React__namespace.createElement("span", {
|
|
24631
|
+
"aria-hidden": "true"
|
|
24632
|
+
}, letter)), crossedOut && /*#__PURE__*/React__namespace.createElement(CrossOutLine, {
|
|
24633
|
+
color: borderColor,
|
|
24634
|
+
sat: true
|
|
24635
|
+
}));
|
|
24636
|
+
}
|
|
24637
|
+
|
|
24638
|
+
const styles$u = aphrodite.StyleSheet.create({
|
|
24639
|
+
satCircle: {
|
|
24640
|
+
display: "block",
|
|
24641
|
+
borderStyle: "solid",
|
|
24642
|
+
borderWidth: 2,
|
|
24643
|
+
content: "''",
|
|
24644
|
+
height: SAT_ICON_SIZE,
|
|
24645
|
+
width: SAT_ICON_SIZE,
|
|
24646
|
+
top: 1,
|
|
24647
|
+
left: 1
|
|
24765
24648
|
},
|
|
24766
24649
|
letter: {
|
|
24767
24650
|
// These properties make sure that this element has the exact
|
|
@@ -24781,6 +24664,118 @@ const styles$t = aphrodite.StyleSheet.create({
|
|
|
24781
24664
|
}
|
|
24782
24665
|
});
|
|
24783
24666
|
|
|
24667
|
+
/* Component for rendering a letter icon in radio choice */
|
|
24668
|
+
|
|
24669
|
+
function getA11yText(letter, checked, correct, crossedOut, showCorrectness) {
|
|
24670
|
+
// There are two pieces of metadata we want to add to each a11yText:
|
|
24671
|
+
// whether the answer was checked/crossed-out/neither, and whether the
|
|
24672
|
+
// answer is correct/incorrect/not-yet-revealed.
|
|
24673
|
+
//
|
|
24674
|
+
// Translation is tricky for cross-product situations like this, so
|
|
24675
|
+
// we've just enumerated all 9 possibilities as separate strings.
|
|
24676
|
+
if (showCorrectness && correct) {
|
|
24677
|
+
if (checked) {
|
|
24678
|
+
return i18n__namespace._("(Choice %(letter)s, Checked, Correct)", {
|
|
24679
|
+
letter
|
|
24680
|
+
});
|
|
24681
|
+
}
|
|
24682
|
+
|
|
24683
|
+
if (crossedOut) {
|
|
24684
|
+
return i18n__namespace._("(Choice %(letter)s, Crossed out, Correct)", {
|
|
24685
|
+
letter
|
|
24686
|
+
});
|
|
24687
|
+
}
|
|
24688
|
+
|
|
24689
|
+
return i18n__namespace._("(Choice %(letter)s, Correct)", {
|
|
24690
|
+
letter
|
|
24691
|
+
});
|
|
24692
|
+
}
|
|
24693
|
+
|
|
24694
|
+
if (showCorrectness && !correct) {
|
|
24695
|
+
if (checked) {
|
|
24696
|
+
return i18n__namespace._("(Choice %(letter)s, Checked, Incorrect)", {
|
|
24697
|
+
letter
|
|
24698
|
+
});
|
|
24699
|
+
}
|
|
24700
|
+
|
|
24701
|
+
if (crossedOut) {
|
|
24702
|
+
return i18n__namespace._("(Choice %(letter)s, Crossed out, Incorrect)", {
|
|
24703
|
+
letter
|
|
24704
|
+
});
|
|
24705
|
+
}
|
|
24706
|
+
|
|
24707
|
+
return i18n__namespace._("(Choice %(letter)s, Incorrect)", {
|
|
24708
|
+
letter
|
|
24709
|
+
});
|
|
24710
|
+
}
|
|
24711
|
+
|
|
24712
|
+
if (checked) {
|
|
24713
|
+
return i18n__namespace._("(Choice %(letter)s, Checked)", {
|
|
24714
|
+
letter
|
|
24715
|
+
});
|
|
24716
|
+
}
|
|
24717
|
+
|
|
24718
|
+
if (crossedOut) {
|
|
24719
|
+
return i18n__namespace._("(Choice %(letter)s, Crossed out)", {
|
|
24720
|
+
letter
|
|
24721
|
+
});
|
|
24722
|
+
}
|
|
24723
|
+
|
|
24724
|
+
return i18n__namespace._("(Choice %(letter)s)", {
|
|
24725
|
+
letter
|
|
24726
|
+
});
|
|
24727
|
+
}
|
|
24728
|
+
|
|
24729
|
+
function ChoiceIcon(props) {
|
|
24730
|
+
const {
|
|
24731
|
+
pos,
|
|
24732
|
+
reviewMode,
|
|
24733
|
+
checked,
|
|
24734
|
+
crossedOut,
|
|
24735
|
+
correct,
|
|
24736
|
+
product,
|
|
24737
|
+
multipleSelect,
|
|
24738
|
+
showCorrectness,
|
|
24739
|
+
focused,
|
|
24740
|
+
hovered,
|
|
24741
|
+
pressed,
|
|
24742
|
+
primaryProductColor = Color__default["default"].blue,
|
|
24743
|
+
previouslyAnswered,
|
|
24744
|
+
transparentBackground
|
|
24745
|
+
} = props;
|
|
24746
|
+
const letter = getChoiceLetter(pos);
|
|
24747
|
+
const a11yText = getA11yText(letter, checked, correct, crossedOut, showCorrectness);
|
|
24748
|
+
|
|
24749
|
+
if (product === "sat") {
|
|
24750
|
+
return /*#__PURE__*/React__namespace.createElement(SATChoiceIcon, {
|
|
24751
|
+
letter: letter,
|
|
24752
|
+
a11yText: a11yText,
|
|
24753
|
+
reviewMode: reviewMode,
|
|
24754
|
+
checked: checked,
|
|
24755
|
+
correct: correct,
|
|
24756
|
+
crossedOut: crossedOut,
|
|
24757
|
+
multipleSelect: multipleSelect
|
|
24758
|
+
});
|
|
24759
|
+
}
|
|
24760
|
+
|
|
24761
|
+
return /*#__PURE__*/React__namespace.createElement(LibraryChoiceIcon, {
|
|
24762
|
+
letter: letter,
|
|
24763
|
+
a11yText: a11yText,
|
|
24764
|
+
reviewMode: reviewMode,
|
|
24765
|
+
checked: checked,
|
|
24766
|
+
crossedOut: crossedOut,
|
|
24767
|
+
focused: focused,
|
|
24768
|
+
hovered: hovered,
|
|
24769
|
+
pressed: pressed,
|
|
24770
|
+
correct: correct,
|
|
24771
|
+
showCorrectness: showCorrectness,
|
|
24772
|
+
primaryProductColor: primaryProductColor,
|
|
24773
|
+
previouslyAnswered: previouslyAnswered,
|
|
24774
|
+
transparentBackground: transparentBackground,
|
|
24775
|
+
multipleSelect: multipleSelect
|
|
24776
|
+
});
|
|
24777
|
+
}
|
|
24778
|
+
|
|
24784
24779
|
/**
|
|
24785
24780
|
* Renders text indicating whether the choice was correct or
|
|
24786
24781
|
* not and whether the choice was selected or not.
|
|
@@ -24789,63 +24784,68 @@ const styles$t = aphrodite.StyleSheet.create({
|
|
|
24789
24784
|
* between the states more immediately clear to users.
|
|
24790
24785
|
*/
|
|
24791
24786
|
|
|
24792
|
-
|
|
24793
|
-
|
|
24794
|
-
|
|
24795
|
-
|
|
24796
|
-
|
|
24797
|
-
|
|
24798
|
-
}
|
|
24787
|
+
function renderText(checked, correct, crossedOut) {
|
|
24788
|
+
if (correct) {
|
|
24789
|
+
// For correct answers, we surface checked _or_ crossedOut state,
|
|
24790
|
+
// because any interaction with the correct answer is noteworthy!
|
|
24791
|
+
if (checked) {
|
|
24792
|
+
return i18n__namespace._("Correct (selected)");
|
|
24793
|
+
}
|
|
24799
24794
|
|
|
24800
|
-
if (
|
|
24801
|
-
|
|
24802
|
-
|
|
24803
|
-
if (checked) {
|
|
24804
|
-
return i18n__namespace._("Correct (selected)");
|
|
24805
|
-
}
|
|
24795
|
+
if (crossedOut) {
|
|
24796
|
+
return i18n__namespace._("Correct (but you crossed it out)");
|
|
24797
|
+
}
|
|
24806
24798
|
|
|
24807
|
-
|
|
24808
|
-
|
|
24809
|
-
|
|
24799
|
+
return i18n__namespace._("Correct");
|
|
24800
|
+
} // But, for incorrect answers, we only surface checked state,
|
|
24801
|
+
// because crossing out an incorrect answer is not noteworthy.
|
|
24810
24802
|
|
|
24811
|
-
return i18n__namespace._("Correct");
|
|
24812
|
-
} // But, for incorrect answers, we only surface checked state,
|
|
24813
|
-
// because crossing out an incorrect answer is not noteworthy.
|
|
24814
24803
|
|
|
24804
|
+
if (checked) {
|
|
24805
|
+
return i18n__namespace._("Incorrect (selected)");
|
|
24806
|
+
}
|
|
24815
24807
|
|
|
24816
|
-
|
|
24817
|
-
|
|
24818
|
-
}
|
|
24808
|
+
return i18n__namespace._("Incorrect");
|
|
24809
|
+
}
|
|
24819
24810
|
|
|
24820
|
-
|
|
24811
|
+
function OptionStatus(props) {
|
|
24812
|
+
const {
|
|
24813
|
+
checked,
|
|
24814
|
+
correct,
|
|
24815
|
+
crossedOut,
|
|
24816
|
+
previouslyAnswered,
|
|
24817
|
+
reviewMode,
|
|
24818
|
+
satStyling
|
|
24819
|
+
} = props; // Option status is exclued for SAT
|
|
24820
|
+
|
|
24821
|
+
if (satStyling) {
|
|
24822
|
+
return null;
|
|
24823
|
+
} // Option status is shown only in review mode, or for incorrectly
|
|
24824
|
+
// answered items.
|
|
24825
|
+
|
|
24826
|
+
|
|
24827
|
+
if (!reviewMode && !previouslyAnswered) {
|
|
24828
|
+
return null;
|
|
24821
24829
|
}
|
|
24822
24830
|
|
|
24823
|
-
|
|
24824
|
-
const {
|
|
24825
|
-
checked,
|
|
24826
|
-
correct,
|
|
24827
|
-
previouslyAnswered
|
|
24828
|
-
} = this.props;
|
|
24829
|
-
let textStyle;
|
|
24831
|
+
let textStyle;
|
|
24830
24832
|
|
|
24831
|
-
|
|
24832
|
-
|
|
24833
|
+
if (correct) {
|
|
24834
|
+
textStyle = styles$t.correct;
|
|
24835
|
+
} else {
|
|
24836
|
+
if (checked || previouslyAnswered) {
|
|
24837
|
+
textStyle = styles$t.incorrectAnswered;
|
|
24833
24838
|
} else {
|
|
24834
|
-
|
|
24835
|
-
textStyle = styles$s.incorrectAnswered;
|
|
24836
|
-
} else {
|
|
24837
|
-
textStyle = styles$s.incorrect;
|
|
24838
|
-
}
|
|
24839
|
+
textStyle = styles$t.incorrect;
|
|
24839
24840
|
}
|
|
24840
|
-
|
|
24841
|
-
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24842
|
-
className: aphrodite.css(styles$s.text, textStyle)
|
|
24843
|
-
}, this._renderText());
|
|
24844
24841
|
}
|
|
24845
24842
|
|
|
24843
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24844
|
+
className: aphrodite.css(styles$t.text, textStyle)
|
|
24845
|
+
}, renderText(checked, correct, crossedOut));
|
|
24846
24846
|
}
|
|
24847
24847
|
|
|
24848
|
-
const styles$
|
|
24848
|
+
const styles$t = aphrodite.StyleSheet.create({
|
|
24849
24849
|
text: {
|
|
24850
24850
|
alignItems: "center",
|
|
24851
24851
|
display: "flex",
|
|
@@ -24872,316 +24872,206 @@ const ellipsisHorizontalIcon = {
|
|
|
24872
24872
|
height: 27.284
|
|
24873
24873
|
};
|
|
24874
24874
|
|
|
24875
|
-
|
|
24876
|
-
|
|
24877
|
-
|
|
24878
|
-
|
|
24879
|
-
|
|
24880
|
-
|
|
24881
|
-
|
|
24882
|
-
|
|
24883
|
-
|
|
24884
|
-
|
|
24885
|
-
|
|
24886
|
-
|
|
24887
|
-
|
|
24888
|
-
|
|
24889
|
-
|
|
24890
|
-
|
|
24891
|
-
|
|
24892
|
-
|
|
24893
|
-
|
|
24894
|
-
|
|
24895
|
-
|
|
24896
|
-
|
|
24897
|
-
|
|
24898
|
-
|
|
24899
|
-
|
|
24900
|
-
|
|
24901
|
-
|
|
24902
|
-
|
|
24903
|
-
|
|
24904
|
-
|
|
24905
|
-
} // Option status is shown only in review mode, or for incorrectly
|
|
24906
|
-
// answered items.
|
|
24907
|
-
|
|
24908
|
-
|
|
24909
|
-
if (!reviewMode && !previouslyAnswered) {
|
|
24910
|
-
return null;
|
|
24911
|
-
}
|
|
24912
|
-
|
|
24913
|
-
return /*#__PURE__*/React__namespace.createElement(OptionStatus, {
|
|
24914
|
-
checked: checked,
|
|
24915
|
-
correct: correct,
|
|
24916
|
-
crossedOut: crossedOut,
|
|
24917
|
-
previouslyAnswered: previouslyAnswered
|
|
24918
|
-
});
|
|
24919
|
-
});
|
|
24920
|
-
|
|
24921
|
-
_defineProperty(this, "renderChoiceIcon", _ref => {
|
|
24922
|
-
let {
|
|
24923
|
-
isFocused,
|
|
24924
|
-
isPressed
|
|
24925
|
-
} = _ref;
|
|
24926
|
-
const {
|
|
24927
|
-
radioStyleVersion,
|
|
24928
|
-
primaryProductColor
|
|
24929
|
-
} = this.props.apiOptions.styling;
|
|
24930
|
-
const finalStyles = typeof radioStyleVersion === "undefined" ? false : radioStyleVersion === "final";
|
|
24931
|
-
|
|
24932
|
-
if (!finalStyles && !this.props.apiOptions.satStyling) {
|
|
24933
|
-
return null;
|
|
24934
|
-
}
|
|
24935
|
-
|
|
24936
|
-
return /*#__PURE__*/React__namespace.createElement(ChoiceIcon, {
|
|
24937
|
-
pos: this.props.pos,
|
|
24938
|
-
correct: this.props.correct,
|
|
24939
|
-
crossedOut: this.props.crossedOut,
|
|
24940
|
-
pressed: isPressed,
|
|
24941
|
-
focused: isFocused,
|
|
24942
|
-
checked: this.props.checked,
|
|
24943
|
-
showCorrectness: this.props.showCorrectness,
|
|
24944
|
-
reviewMode: this.props.reviewMode,
|
|
24945
|
-
product: this.props.apiOptions.satStyling ? "sat" : "library",
|
|
24946
|
-
primaryProductColor: primaryProductColor,
|
|
24947
|
-
previouslyAnswered: this.props.previouslyAnswered
|
|
24948
|
-
});
|
|
24949
|
-
});
|
|
24875
|
+
function Choice(props) {
|
|
24876
|
+
const {
|
|
24877
|
+
disabled,
|
|
24878
|
+
checked,
|
|
24879
|
+
content,
|
|
24880
|
+
crossedOut,
|
|
24881
|
+
showCorrectness,
|
|
24882
|
+
multipleSelect,
|
|
24883
|
+
onChange,
|
|
24884
|
+
reviewMode,
|
|
24885
|
+
correct,
|
|
24886
|
+
apiOptions,
|
|
24887
|
+
previouslyAnswered,
|
|
24888
|
+
pos,
|
|
24889
|
+
showRationale,
|
|
24890
|
+
rationale,
|
|
24891
|
+
forwardedRef
|
|
24892
|
+
} = props;
|
|
24893
|
+
const [isInputFocused, setIsInputFocused] = React.useState(false);
|
|
24894
|
+
React.useEffect(() => {
|
|
24895
|
+
if (isInputFocused && disabled) {
|
|
24896
|
+
setIsInputFocused(false);
|
|
24897
|
+
}
|
|
24898
|
+
}, [disabled, isInputFocused, setIsInputFocused]); // Call `this.props.onChange` with the given values. Any keys that are not
|
|
24899
|
+
// specified will be filled in with the current value. (For example, if
|
|
24900
|
+
// `checked` is specified but `crossedOut` is not, then `crossedOut` will
|
|
24901
|
+
// be filled in with `this.props.crossedOut`.)
|
|
24902
|
+
//
|
|
24903
|
+
// This enables us to use shorthand inside this component, while
|
|
24904
|
+
// maintaining a consistent API for the parent.
|
|
24950
24905
|
|
|
24951
|
-
|
|
24952
|
-
|
|
24906
|
+
function sendChange(newValues) {
|
|
24907
|
+
var _newValues$checked, _newValues$crossedOut;
|
|
24953
24908
|
|
|
24954
|
-
|
|
24955
|
-
|
|
24956
|
-
|
|
24957
|
-
|
|
24958
|
-
|
|
24959
|
-
});
|
|
24909
|
+
const updatedChecked = (_newValues$checked = newValues.checked) !== null && _newValues$checked !== void 0 ? _newValues$checked : checked;
|
|
24910
|
+
const updatedCrossedOut = (_newValues$crossedOut = newValues.crossedOut) !== null && _newValues$crossedOut !== void 0 ? _newValues$crossedOut : crossedOut;
|
|
24911
|
+
onChange({
|
|
24912
|
+
checked: updatedChecked,
|
|
24913
|
+
crossedOut: updatedCrossedOut
|
|
24960
24914
|
});
|
|
24915
|
+
} // HACK: while most of the styling for rendering SAT items is handled
|
|
24916
|
+
// via aphrodite, we also need to assign normal CSS classnames here to
|
|
24917
|
+
// special-case the coloring of MathJax formulas (see .MathJax .math in
|
|
24918
|
+
// stylesheets/task-package/tasks.less)
|
|
24961
24919
|
|
|
24962
|
-
_defineProperty(this, "focusInput", () => {
|
|
24963
|
-
var _this$_input;
|
|
24964
24920
|
|
|
24965
|
-
|
|
24966
|
-
|
|
24967
|
-
|
|
24921
|
+
const satCorrectChoice = apiOptions.satStyling && reviewMode && correct;
|
|
24922
|
+
const satIncorrectChecked = apiOptions.satStyling && reviewMode && !correct && checked;
|
|
24923
|
+
const descriptionClassName = classNames__default["default"]("description", satCorrectChoice && "sat-correct", satIncorrectChecked && "sat-incorrect", aphrodite.css(!apiOptions.satStyling && styles$s.description, apiOptions.satStyling && styles$s.satDescription, satCorrectChoice && styles$s.satDescriptionCorrect, satCorrectChoice && checked && styles$s.satDescriptionCorrectChecked, satIncorrectChecked && styles$s.satDescriptionIncorrectChecked));
|
|
24924
|
+
const rationaleClassName = classNames__default["default"]("perseus-radio-rationale-content", aphrodite.css(styles$s.rationale, !apiOptions.satStyling && styles$s.nonSatRationale, apiOptions.satStyling && styles$s.satReviewRationale)); // We want to show the choices as dimmed out when the choices are
|
|
24925
|
+
// disabled. However, we don't want to do this in the SAT product and
|
|
24926
|
+
// we also don't want to do this when we're in review mode in the
|
|
24927
|
+
// content library.
|
|
24968
24928
|
|
|
24969
|
-
|
|
24970
|
-
|
|
24971
|
-
|
|
24972
|
-
|
|
24973
|
-
|
|
24929
|
+
const showDimmed = !apiOptions.satStyling && !reviewMode && apiOptions.readOnly || crossedOut;
|
|
24930
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24931
|
+
style: {
|
|
24932
|
+
dispay: "flex",
|
|
24933
|
+
flexDirection: "column"
|
|
24974
24934
|
}
|
|
24975
|
-
}
|
|
24935
|
+
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
24936
|
+
style: {
|
|
24937
|
+
display: "flex",
|
|
24938
|
+
flexDirection: "row",
|
|
24939
|
+
width: "100%",
|
|
24940
|
+
opacity: showDimmed ? 0.5 : 1.0
|
|
24941
|
+
}
|
|
24942
|
+
}, /*#__PURE__*/React__namespace.createElement(Clickable__default["default"], {
|
|
24943
|
+
onClick: () => {
|
|
24944
|
+
// If we're checking a crossed-out option, let's
|
|
24945
|
+
// also uncross it.
|
|
24946
|
+
sendChange({
|
|
24947
|
+
checked: !checked,
|
|
24948
|
+
crossedOut: false
|
|
24949
|
+
});
|
|
24950
|
+
},
|
|
24951
|
+
className: descriptionClassName,
|
|
24952
|
+
disabled: disabled || apiOptions.staticRender || apiOptions.readOnly,
|
|
24953
|
+
"aria-label": "Select Choice ".concat(getChoiceLetter(pos)),
|
|
24954
|
+
"aria-checked": checked ? "true" : "false",
|
|
24955
|
+
role: "checkbox",
|
|
24956
|
+
style: {
|
|
24957
|
+
flex: 1
|
|
24958
|
+
},
|
|
24959
|
+
ref: forwardedRef
|
|
24960
|
+
}, _ref => {
|
|
24961
|
+
var _apiOptions$styling;
|
|
24976
24962
|
|
|
24977
|
-
|
|
24978
|
-
|
|
24979
|
-
|
|
24980
|
-
|
|
24981
|
-
|
|
24982
|
-
checked
|
|
24983
|
-
} = this.props; // HACK: while most of the styling for rendering SAT items is handled
|
|
24984
|
-
// via aphrodite, we also need to assign normal CSS classnames here to
|
|
24985
|
-
// special-case the coloring of MathJax formulas (see .MathJax .math in
|
|
24986
|
-
// stylesheets/task-package/tasks.less)
|
|
24987
|
-
|
|
24988
|
-
const satCorrectChoice = sat && reviewMode && correct;
|
|
24989
|
-
const satIncorrectChecked = sat && reviewMode && !correct && checked;
|
|
24990
|
-
const descriptionClassName = classNames__default["default"]("description", satCorrectChoice && "sat-correct", satIncorrectChecked && "sat-incorrect", aphrodite.css(!sat && styles$r.description, sat && styles$r.satDescription, satCorrectChoice && styles$r.satDescriptionCorrect, satCorrectChoice && checked && styles$r.satDescriptionCorrectChecked, satIncorrectChecked && styles$r.satDescriptionIncorrectChecked));
|
|
24991
|
-
const rationaleClassName = classNames__default["default"]("perseus-radio-rationale-content", aphrodite.css(styles$r.rationale, !sat && styles$r.nonSatRationale, sat && styles$r.satReviewRationale)); // We want to show the choices as dimmed out when the choices are
|
|
24992
|
-
// disabled. However, we don't want to do this in the SAT product and
|
|
24993
|
-
// we also don't want to do this when we're in review mode in the
|
|
24994
|
-
// content library.
|
|
24995
|
-
|
|
24996
|
-
const showDimmed = !sat && !reviewMode && this.props.apiOptions.readOnly || this.props.crossedOut;
|
|
24963
|
+
let {
|
|
24964
|
+
hovered,
|
|
24965
|
+
focused,
|
|
24966
|
+
pressed
|
|
24967
|
+
} = _ref;
|
|
24997
24968
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24998
|
-
style: {
|
|
24999
|
-
dispay: "flex",
|
|
25000
|
-
flexDirection: "column"
|
|
25001
|
-
}
|
|
25002
|
-
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
25003
24969
|
style: {
|
|
25004
24970
|
display: "flex",
|
|
25005
24971
|
flexDirection: "row",
|
|
25006
|
-
|
|
25007
|
-
|
|
24972
|
+
justifyContent: "center",
|
|
24973
|
+
alignContent: "center",
|
|
24974
|
+
paddingTop: Spacing__default["default"].xSmall_8,
|
|
24975
|
+
paddingBottom: Spacing__default["default"].xSmall_8
|
|
25008
24976
|
}
|
|
25009
|
-
}, /*#__PURE__*/React__namespace.createElement(
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
|
|
25015
|
-
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25019
|
-
|
|
25020
|
-
|
|
25021
|
-
|
|
25022
|
-
|
|
24977
|
+
}, /*#__PURE__*/React__namespace.createElement(ChoiceIcon, {
|
|
24978
|
+
pos: pos,
|
|
24979
|
+
correct: correct,
|
|
24980
|
+
crossedOut: crossedOut,
|
|
24981
|
+
pressed: pressed,
|
|
24982
|
+
focused: focused,
|
|
24983
|
+
checked: checked,
|
|
24984
|
+
hovered: hovered,
|
|
24985
|
+
showCorrectness: showCorrectness,
|
|
24986
|
+
multipleSelect: multipleSelect,
|
|
24987
|
+
reviewMode: reviewMode,
|
|
24988
|
+
product: apiOptions.satStyling ? "sat" : "library",
|
|
24989
|
+
primaryProductColor: (_apiOptions$styling = apiOptions.styling) === null || _apiOptions$styling === void 0 ? void 0 : _apiOptions$styling.primaryProductColor,
|
|
24990
|
+
previouslyAnswered: previouslyAnswered
|
|
24991
|
+
}), /*#__PURE__*/React__namespace.createElement("span", {
|
|
25023
24992
|
style: {
|
|
25024
|
-
|
|
24993
|
+
paddingLeft: Spacing__default["default"].small_12,
|
|
24994
|
+
textAlign: "left",
|
|
24995
|
+
flex: 1,
|
|
24996
|
+
paddingTop: 4
|
|
25025
24997
|
}
|
|
25026
|
-
},
|
|
24998
|
+
}, /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement(OptionStatus, {
|
|
24999
|
+
checked: checked,
|
|
25000
|
+
correct: correct,
|
|
25001
|
+
crossedOut: crossedOut,
|
|
25002
|
+
previouslyAnswered: previouslyAnswered,
|
|
25003
|
+
reviewMode: reviewMode,
|
|
25004
|
+
satStyling: apiOptions.satStyling
|
|
25005
|
+
})), /*#__PURE__*/React__namespace.createElement("div", null, content)));
|
|
25006
|
+
}), apiOptions.crossOutEnabled && !reviewMode && /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.Popover, {
|
|
25007
|
+
dismissEnabled: true,
|
|
25008
|
+
content: _ref2 => {
|
|
25027
25009
|
let {
|
|
25028
|
-
|
|
25029
|
-
focused,
|
|
25030
|
-
pressed
|
|
25010
|
+
close
|
|
25031
25011
|
} = _ref2;
|
|
25032
|
-
return /*#__PURE__*/React__namespace.createElement(
|
|
25033
|
-
|
|
25034
|
-
|
|
25035
|
-
|
|
25036
|
-
|
|
25037
|
-
|
|
25038
|
-
|
|
25039
|
-
|
|
25040
|
-
|
|
25041
|
-
|
|
25042
|
-
|
|
25043
|
-
|
|
25044
|
-
|
|
25045
|
-
|
|
25046
|
-
|
|
25047
|
-
|
|
25048
|
-
|
|
25049
|
-
|
|
25050
|
-
|
|
25051
|
-
|
|
25052
|
-
|
|
25053
|
-
|
|
25054
|
-
|
|
25055
|
-
|
|
25056
|
-
close
|
|
25057
|
-
} = _ref3;
|
|
25058
|
-
return /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.PopoverContent, {
|
|
25059
|
-
title: "Cross out",
|
|
25060
|
-
content: "Cross out option",
|
|
25061
|
-
closeButtonVisible: true,
|
|
25062
|
-
actions: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksLayout.Strut, {
|
|
25063
|
-
size: Spacing__default["default"].medium_16
|
|
25064
|
-
}), /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
|
|
25065
|
-
kind: "primary",
|
|
25066
|
-
"aria-label": "Cross out Choice ".concat(getChoiceLetter(this.props.pos)),
|
|
25067
|
-
disabled: this.props.apiOptions.readOnly || reviewMode,
|
|
25068
|
-
onClick: () => {
|
|
25069
|
-
if (!this.props.crossedOut) {
|
|
25070
|
-
// If we're crossing
|
|
25071
|
-
// out a checked
|
|
25072
|
-
// option, let's also
|
|
25073
|
-
// uncheck it.
|
|
25074
|
-
this._sendChange({
|
|
25075
|
-
checked: false,
|
|
25076
|
-
crossedOut: true
|
|
25077
|
-
});
|
|
25078
|
-
} else {
|
|
25079
|
-
this._sendChange({
|
|
25080
|
-
crossedOut: false
|
|
25081
|
-
});
|
|
25082
|
-
}
|
|
25083
|
-
|
|
25084
|
-
close();
|
|
25012
|
+
return /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.PopoverContent, {
|
|
25013
|
+
title: "Cross out",
|
|
25014
|
+
content: "Cross out option",
|
|
25015
|
+
closeButtonVisible: true,
|
|
25016
|
+
actions: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksLayout.Strut, {
|
|
25017
|
+
size: Spacing__default["default"].medium_16
|
|
25018
|
+
}), /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
|
|
25019
|
+
kind: "primary",
|
|
25020
|
+
"aria-label": "Cross out Choice ".concat(getChoiceLetter(pos)),
|
|
25021
|
+
disabled: apiOptions.readOnly || reviewMode,
|
|
25022
|
+
onClick: () => {
|
|
25023
|
+
if (!crossedOut) {
|
|
25024
|
+
// If we're crossing
|
|
25025
|
+
// out a checked
|
|
25026
|
+
// option, let's also
|
|
25027
|
+
// uncheck it.
|
|
25028
|
+
sendChange({
|
|
25029
|
+
checked: false,
|
|
25030
|
+
crossedOut: true
|
|
25031
|
+
});
|
|
25032
|
+
} else {
|
|
25033
|
+
sendChange({
|
|
25034
|
+
crossedOut: false
|
|
25035
|
+
});
|
|
25085
25036
|
}
|
|
25086
|
-
|
|
25087
|
-
|
|
25088
|
-
|
|
25037
|
+
|
|
25038
|
+
close();
|
|
25039
|
+
}
|
|
25040
|
+
}, crossedOut ? "Bring back" : "Cross out"))
|
|
25041
|
+
});
|
|
25042
|
+
}
|
|
25043
|
+
}, _ref3 => {
|
|
25044
|
+
let {
|
|
25045
|
+
open
|
|
25046
|
+
} = _ref3;
|
|
25047
|
+
return /*#__PURE__*/React__namespace.createElement(Clickable__default["default"], {
|
|
25048
|
+
onClick: open,
|
|
25049
|
+
"aria-label": "Open menu for Choice ".concat(getChoiceLetter(pos)),
|
|
25050
|
+
disabled: apiOptions.staticRender
|
|
25089
25051
|
}, _ref4 => {
|
|
25090
|
-
|
|
25091
|
-
|
|
25092
|
-
|
|
25093
|
-
|
|
25094
|
-
onClick: open,
|
|
25095
|
-
"aria-label": "Open menu for Choice ".concat(getChoiceLetter(this.props.pos)),
|
|
25096
|
-
disabled: this.props.apiOptions.staticRender
|
|
25097
|
-
}, _ref5 => {
|
|
25098
|
-
return /*#__PURE__*/React__namespace.createElement(Icon, {
|
|
25099
|
-
icon: ellipsisHorizontalIcon,
|
|
25100
|
-
size: 3,
|
|
25101
|
-
color: Color__default["default"].offBlack64
|
|
25102
|
-
});
|
|
25052
|
+
return /*#__PURE__*/React__namespace.createElement(Icon, {
|
|
25053
|
+
icon: ellipsisHorizontalIcon,
|
|
25054
|
+
size: 3,
|
|
25055
|
+
color: Color__default["default"].offBlack64
|
|
25103
25056
|
});
|
|
25104
|
-
})
|
|
25105
|
-
|
|
25106
|
-
|
|
25107
|
-
|
|
25108
|
-
}
|
|
25109
|
-
|
|
25057
|
+
});
|
|
25058
|
+
})), showRationale && /*#__PURE__*/React__namespace.createElement("div", {
|
|
25059
|
+
className: rationaleClassName,
|
|
25060
|
+
"data-test-id": "perseus-radio-rationale-content-".concat(pos)
|
|
25061
|
+
}, rationale));
|
|
25110
25062
|
}
|
|
25111
25063
|
|
|
25112
|
-
|
|
25113
|
-
|
|
25114
|
-
// This change will also require make sure that item-renderer.jsx and
|
|
25115
|
-
// server-item-renderer.jsx have appropriate defaults for apiOptions
|
|
25116
|
-
// because many of the properties on Options.propTypes are required.
|
|
25117
|
-
apiOptions: PropTypes__default["default"].shape({
|
|
25118
|
-
satStyling: PropTypes__default["default"].bool,
|
|
25119
|
-
isMobile: PropTypes__default["default"].bool,
|
|
25120
|
-
styling: PropTypes__default["default"].shape({
|
|
25121
|
-
radioStyleVersion: PropTypes__default["default"].oneOf(["intermediate", "final"]),
|
|
25122
|
-
primaryProductColor: PropTypes__default["default"].string
|
|
25123
|
-
}),
|
|
25124
|
-
readOnly: PropTypes__default["default"].bool
|
|
25125
|
-
}),
|
|
25126
|
-
checked: PropTypes__default["default"].bool,
|
|
25127
|
-
className: PropTypes__default["default"].string,
|
|
25128
|
-
rationale: PropTypes__default["default"].node,
|
|
25129
|
-
content: PropTypes__default["default"].node,
|
|
25130
|
-
correct: PropTypes__default["default"].bool,
|
|
25131
|
-
deselectEnabled: PropTypes__default["default"].bool,
|
|
25132
|
-
disabled: PropTypes__default["default"].bool,
|
|
25133
|
-
editMode: PropTypes__default["default"].bool,
|
|
25134
|
-
groupName: PropTypes__default["default"].string,
|
|
25135
|
-
isLastChoice: PropTypes__default["default"].bool,
|
|
25136
|
-
// Needed for border styling
|
|
25137
|
-
// This indicates the position of the choice relative to others
|
|
25138
|
-
// (so that we can display a nice little (A), (B), etc. next to it)
|
|
25139
|
-
// Also used to generate an id for each input.
|
|
25140
|
-
pos: PropTypes__default["default"].number,
|
|
25141
|
-
reviewMode: PropTypes__default["default"].bool,
|
|
25142
|
-
showRationale: PropTypes__default["default"].bool,
|
|
25143
|
-
showCorrectness: PropTypes__default["default"].bool,
|
|
25144
|
-
type: PropTypes__default["default"].string,
|
|
25145
|
-
// Indicates whether the user has "crossed out" this choice, meaning
|
|
25146
|
-
// that they don't think it's correct. This value does not affect
|
|
25147
|
-
// scoring or other behavior; it's just a note for the user's
|
|
25148
|
-
// reference.
|
|
25149
|
-
crossedOut: PropTypes__default["default"].bool,
|
|
25150
|
-
// Indicates that the user has previously selected this answer. These
|
|
25151
|
-
// answers may be rendered orange in review, rather than grey if
|
|
25152
|
-
// incorrect.
|
|
25153
|
-
previouslyAnswered: PropTypes__default["default"].bool,
|
|
25154
|
-
// A callback indicating that this choice has changed. Its argument is
|
|
25155
|
-
// an object with two keys: `checked` and `crossedOut`. Each contains a
|
|
25156
|
-
// boolean value specifying the new checked and crossed-out value of
|
|
25157
|
-
// this choice.
|
|
25158
|
-
onChange: PropTypes__default["default"].func,
|
|
25159
|
-
// Callbacks for when the user presses the up arrow or down arrow
|
|
25160
|
-
// (respectively) to navigate between choices. When called, the parent
|
|
25161
|
-
// finds the prev/next choice, focuses the input, and checks the radio
|
|
25162
|
-
// button if appropriate. (The reason we have custom behavior is that
|
|
25163
|
-
// we actually _don't_ want to auto-select crossed-out choices!)
|
|
25164
|
-
goToPrevChoice: PropTypes__default["default"].func.isRequired,
|
|
25165
|
-
goToNextChoice: PropTypes__default["default"].func.isRequired
|
|
25166
|
-
});
|
|
25167
|
-
|
|
25168
|
-
_defineProperty(Choice, "defaultProps", {
|
|
25169
|
-
apiOptions: {
|
|
25170
|
-
styling: {}
|
|
25171
|
-
},
|
|
25064
|
+
Choice.defaultProps = {
|
|
25065
|
+
disabled: false,
|
|
25172
25066
|
checked: false,
|
|
25173
|
-
|
|
25067
|
+
onChange: newValues => {},
|
|
25174
25068
|
correct: false,
|
|
25175
|
-
|
|
25176
|
-
|
|
25177
|
-
onChange: function () {},
|
|
25178
|
-
showRationale: false,
|
|
25179
|
-
type: "radio",
|
|
25069
|
+
apiOptions: ApiOptions.defaults,
|
|
25070
|
+
previouslyAnswered: false,
|
|
25180
25071
|
pos: 0,
|
|
25181
|
-
|
|
25182
|
-
}
|
|
25183
|
-
|
|
25184
|
-
const styles$r = aphrodite.StyleSheet.create({
|
|
25072
|
+
showRationale: false
|
|
25073
|
+
};
|
|
25074
|
+
const styles$s = aphrodite.StyleSheet.create({
|
|
25185
25075
|
description: {
|
|
25186
25076
|
display: "inline-block",
|
|
25187
25077
|
width: "100%"
|
|
@@ -25221,184 +25111,83 @@ const styles$r = aphrodite.StyleSheet.create({
|
|
|
25221
25111
|
marginLeft: 45
|
|
25222
25112
|
}
|
|
25223
25113
|
});
|
|
25114
|
+
var Choice$1 = /*#__PURE__*/React__namespace.forwardRef((props, ref) => /*#__PURE__*/React__namespace.createElement(Choice, _extends({}, props, {
|
|
25115
|
+
forwardedRef: ref
|
|
25116
|
+
})));
|
|
25224
25117
|
|
|
25225
|
-
|
|
25226
|
-
|
|
25227
|
-
|
|
25228
|
-
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
|
|
25232
|
-
|
|
25233
|
-
|
|
25234
|
-
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
|
|
25249
|
-
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
};
|
|
25254
|
-
return /*#__PURE__*/React__namespace.createElement(Choice, choiceProps);
|
|
25255
|
-
}
|
|
25256
|
-
|
|
25118
|
+
function ChoiceNoneAbove(props) {
|
|
25119
|
+
const {
|
|
25120
|
+
showContent,
|
|
25121
|
+
content,
|
|
25122
|
+
forwardedRef,
|
|
25123
|
+
...rest
|
|
25124
|
+
} = props;
|
|
25125
|
+
const choiceProps = { ...rest,
|
|
25126
|
+
content: showContent ? content :
|
|
25127
|
+
/*#__PURE__*/
|
|
25128
|
+
// We use a Renderer here because that is how
|
|
25129
|
+
// `this.props.content` is wrapped otherwise.
|
|
25130
|
+
// We pass in a key here so that we avoid a semi-spurious
|
|
25131
|
+
// react warning when we render this in the same place
|
|
25132
|
+
// as the previous choice content renderer.
|
|
25133
|
+
// Note this destroys state, but since all we're doing
|
|
25134
|
+
// is outputting "None of the above", that is okay.
|
|
25135
|
+
//
|
|
25136
|
+
// todo(matthewc): this seems like way overkill
|
|
25137
|
+
// just to render a string
|
|
25138
|
+
React__namespace.createElement(Renderer, {
|
|
25139
|
+
key: "noneOfTheAboveRenderer",
|
|
25140
|
+
content: i18n__namespace._("None of the above")
|
|
25141
|
+
})
|
|
25142
|
+
};
|
|
25143
|
+
return /*#__PURE__*/React__namespace.createElement(Choice$1, _extends({}, choiceProps, {
|
|
25144
|
+
ref: forwardedRef
|
|
25145
|
+
}));
|
|
25257
25146
|
}
|
|
25258
25147
|
|
|
25259
|
-
|
|
25260
|
-
className: PropTypes__default["default"].string,
|
|
25261
|
-
content: PropTypes__default["default"].node,
|
|
25262
|
-
showContent: PropTypes__default["default"].bool
|
|
25263
|
-
});
|
|
25264
|
-
|
|
25265
|
-
_defineProperty(ChoiceNoneAbove, "defaultProps", {
|
|
25148
|
+
ChoiceNoneAbove.defaultProps = {
|
|
25266
25149
|
showContent: true
|
|
25267
|
-
}
|
|
25150
|
+
};
|
|
25151
|
+
var ChoiceNoneAbove$1 = /*#__PURE__*/React__namespace.forwardRef((props, ref) => /*#__PURE__*/React__namespace.createElement(ChoiceNoneAbove, _extends({}, props, {
|
|
25152
|
+
forwardedRef: ref
|
|
25153
|
+
})));
|
|
25268
25154
|
|
|
25269
25155
|
const {
|
|
25270
25156
|
captureScratchpadTouchStart: captureScratchpadTouchStart$2
|
|
25271
|
-
} = Util;
|
|
25272
|
-
const ChoicesType = PropTypes__default["default"].arrayOf(PropTypes__default["default"].shape({
|
|
25273
|
-
// Indicates whether this choice is checked.
|
|
25274
|
-
checked: PropTypes__default["default"].bool,
|
|
25275
|
-
// Indicates whether the user has "crossed out" this choice, meaning
|
|
25276
|
-
// that they don't think it's correct. This value does not affect
|
|
25277
|
-
// scoring or other behavior; it's just a note for the user's
|
|
25278
|
-
// reference.
|
|
25279
|
-
crossedOut: PropTypes__default["default"].bool,
|
|
25280
|
-
content: PropTypes__default["default"].node,
|
|
25281
|
-
rationale: PropTypes__default["default"].node,
|
|
25282
|
-
hasRationale: PropTypes__default["default"].bool,
|
|
25283
|
-
showRationale: PropTypes__default["default"].bool,
|
|
25284
|
-
showCorrectness: PropTypes__default["default"].bool,
|
|
25285
|
-
correct: PropTypes__default["default"].bool,
|
|
25286
|
-
originalIndex: PropTypes__default["default"].number,
|
|
25287
|
-
isNoneOfTheAbove: PropTypes__default["default"].bool
|
|
25288
|
-
}));
|
|
25289
|
-
const radioBorderColor = radioBorderColor$2;
|
|
25290
|
-
|
|
25291
|
-
class BaseRadio extends React__namespace.Component {
|
|
25292
|
-
constructor() {
|
|
25293
|
-
super(...arguments);
|
|
25294
|
-
|
|
25295
|
-
_defineProperty(this, "state", {
|
|
25296
|
-
// TODO(mdr): This keeps the ID stable across re-renders on the
|
|
25297
|
-
// same machine, but, at time of writing, the server's state
|
|
25298
|
-
// isn't rehydrated to the client during SSR, so the server and
|
|
25299
|
-
// client will generate different IDs and cause a mismatch
|
|
25300
|
-
// during SSR :(
|
|
25301
|
-
radioGroupName: ___default["default"].uniqueId("perseus_radio_")
|
|
25302
|
-
});
|
|
25303
|
-
|
|
25304
|
-
_defineProperty(this, "updateChoice", (choiceIndex, newValues) => {
|
|
25305
|
-
// Get the baseline `checked` values. If we're checking a new answer
|
|
25306
|
-
// and multiple-select is not on, we should clear all choices to be
|
|
25307
|
-
// unchecked. Otherwise, we should copy the old checked values.
|
|
25308
|
-
let newCheckedList;
|
|
25309
|
-
|
|
25310
|
-
if (newValues.checked && !this.props.multipleSelect) {
|
|
25311
|
-
newCheckedList = this.props.choices.map(_ => false);
|
|
25312
|
-
} else {
|
|
25313
|
-
newCheckedList = this.props.choices.map(c => c.checked);
|
|
25314
|
-
} // Get the baseline `crossedOut` values.
|
|
25157
|
+
} = Util; // exported for tests
|
|
25315
25158
|
|
|
25316
|
-
|
|
25317
|
-
|
|
25318
|
-
|
|
25319
|
-
|
|
25320
|
-
|
|
25321
|
-
this.props.onChange({
|
|
25322
|
-
checked: newCheckedList,
|
|
25323
|
-
crossedOut: newCrossedOutList
|
|
25159
|
+
function getInstructionsText(multipleSelect, countChoices, numCorrect) {
|
|
25160
|
+
if (multipleSelect) {
|
|
25161
|
+
if (countChoices) {
|
|
25162
|
+
return i18n__namespace._("Choose %(numCorrect)s answers:", {
|
|
25163
|
+
numCorrect: numCorrect
|
|
25324
25164
|
});
|
|
25325
|
-
}
|
|
25326
|
-
|
|
25327
|
-
_defineProperty(this, "focus", i => {
|
|
25328
|
-
// $FlowFixMe[incompatible-use]
|
|
25329
|
-
// $FlowFixMe[prop-missing]
|
|
25330
|
-
ReactDOM__default["default"].findDOMNode(this.refs["radio" + (i || 0)]).focus(); // eslint-disable-line react/no-string-refs
|
|
25331
|
-
|
|
25332
|
-
return true;
|
|
25333
|
-
});
|
|
25334
|
-
|
|
25335
|
-
_defineProperty(this, "getInstructionsText", () => {
|
|
25336
|
-
if (this.props.multipleSelect) {
|
|
25337
|
-
if (this.props.countChoices) {
|
|
25338
|
-
return i18n__namespace._("Choose %(numCorrect)s answers:", {
|
|
25339
|
-
numCorrect: this.props.numCorrect
|
|
25340
|
-
});
|
|
25341
|
-
}
|
|
25342
|
-
|
|
25343
|
-
return i18n__namespace._("Choose all answers that apply:");
|
|
25344
|
-
}
|
|
25345
|
-
|
|
25346
|
-
return i18n__namespace._("Choose 1 answer:");
|
|
25347
|
-
});
|
|
25348
|
-
|
|
25349
|
-
_defineProperty(this, "deselectEnabled", () => {
|
|
25350
|
-
// We want to force enable deselect on mobile.
|
|
25351
|
-
return this.props.apiOptions.isMobile || this.props.deselectEnabled;
|
|
25352
|
-
});
|
|
25353
|
-
|
|
25354
|
-
_defineProperty(this, "goToChoice", newChoiceIndex => {
|
|
25355
|
-
const numChoices = this.props.choices.length; // Wrap the newChoiceIndex around the start/end, if necessary.
|
|
25356
|
-
|
|
25357
|
-
if (newChoiceIndex < 0) {
|
|
25358
|
-
newChoiceIndex += numChoices;
|
|
25359
|
-
}
|
|
25360
|
-
|
|
25361
|
-
if (newChoiceIndex >= numChoices) {
|
|
25362
|
-
newChoiceIndex -= numChoices;
|
|
25363
|
-
} // Focus the new choice's input element.
|
|
25364
|
-
// eslint-disable-next-line react/no-string-refs
|
|
25365
|
-
|
|
25366
|
-
|
|
25367
|
-
const choiceInstance = this.refs["radio".concat(newChoiceIndex)];
|
|
25368
|
-
|
|
25369
|
-
if (!choiceInstance) {
|
|
25370
|
-
Log.error("found no choice at index ".concat(newChoiceIndex, ", even after ") + "wrapping it to be within bounds", Errors.Internal, {
|
|
25371
|
-
loggedMetadata: {
|
|
25372
|
-
choices: JSON.stringify(this.props.choices)
|
|
25373
|
-
}
|
|
25374
|
-
});
|
|
25375
|
-
return;
|
|
25376
|
-
}
|
|
25377
|
-
|
|
25378
|
-
choiceInstance.focusInput(); // We aren't going to change any choices' crossed-out state, but we
|
|
25379
|
-
// _are_ going to deselect all choices.
|
|
25380
|
-
|
|
25381
|
-
const newCrossedOutList = this.props.choices.map(c => c.crossedOut);
|
|
25382
|
-
const newCheckedList = this.props.choices.map(c => false); // And, if the new choice isn't crossed out, we'll select it.
|
|
25383
|
-
|
|
25384
|
-
if (!this.props.choices[newChoiceIndex].crossedOut) {
|
|
25385
|
-
newCheckedList[newChoiceIndex] = true;
|
|
25386
|
-
}
|
|
25165
|
+
}
|
|
25387
25166
|
|
|
25388
|
-
|
|
25389
|
-
checked: newCheckedList,
|
|
25390
|
-
crossedOut: newCrossedOutList
|
|
25391
|
-
});
|
|
25392
|
-
});
|
|
25167
|
+
return i18n__namespace._("Choose all answers that apply:");
|
|
25393
25168
|
}
|
|
25394
25169
|
|
|
25395
|
-
|
|
25396
|
-
|
|
25397
|
-
|
|
25398
|
-
|
|
25399
|
-
|
|
25400
|
-
|
|
25401
|
-
|
|
25170
|
+
return i18n__namespace._("Choose 1 answer:");
|
|
25171
|
+
}
|
|
25172
|
+
|
|
25173
|
+
function BaseRadio(props) {
|
|
25174
|
+
const {
|
|
25175
|
+
apiOptions,
|
|
25176
|
+
reviewModeRubric,
|
|
25177
|
+
choices,
|
|
25178
|
+
editMode,
|
|
25179
|
+
multipleSelect,
|
|
25180
|
+
labelWrap,
|
|
25181
|
+
countChoices,
|
|
25182
|
+
numCorrect,
|
|
25183
|
+
isLastUsedWidget,
|
|
25184
|
+
registerFocusFunction
|
|
25185
|
+
} = props; // useEffect doesn't have previous props
|
|
25186
|
+
|
|
25187
|
+
const prevReviewModeRubric = React.useRef();
|
|
25188
|
+
const choiceRefs = React.useRef([]);
|
|
25189
|
+
React.useEffect(() => {
|
|
25190
|
+
// Switching into review mode can sometimes cause the selected answer
|
|
25402
25191
|
// to scroll out of view - for example, when we reveal all those tall
|
|
25403
25192
|
// rationales. This can be disorienting for the user.
|
|
25404
25193
|
//
|
|
@@ -25411,17 +25200,25 @@ class BaseRadio extends React__namespace.Component {
|
|
|
25411
25200
|
// the most recently used widget (because, if the user has since
|
|
25412
25201
|
// touched something else, they're probably not trying to keep their
|
|
25413
25202
|
// eye on this widget anymore).
|
|
25414
|
-
|
|
25415
|
-
if (apiOptions.canScrollPage && isLastUsedWidget && reviewModeRubric && !prevProps.reviewModeRubric) {
|
|
25203
|
+
if (apiOptions.canScrollPage && isLastUsedWidget && reviewModeRubric && !prevReviewModeRubric.current) {
|
|
25416
25204
|
const checkedIndex = choices.findIndex(c => c.checked);
|
|
25417
25205
|
|
|
25418
25206
|
if (checkedIndex >= 0) {
|
|
25419
|
-
const
|
|
25420
|
-
|
|
25421
|
-
|
|
25207
|
+
const ref = choiceRefs.current[checkedIndex]; // note(matthew): we know this is only getting passed
|
|
25208
|
+
// to a WB Clickable button, so we force it to be of
|
|
25209
|
+
// type HTMLButtonElement
|
|
25210
|
+
|
|
25211
|
+
const anyNode = ReactDOM__default["default"].findDOMNode(ref.current);
|
|
25212
|
+
const buttonNode = anyNode;
|
|
25213
|
+
|
|
25214
|
+
if (buttonNode) {
|
|
25215
|
+
scrollElementIntoView(buttonNode);
|
|
25216
|
+
}
|
|
25422
25217
|
}
|
|
25423
25218
|
}
|
|
25424
|
-
|
|
25219
|
+
|
|
25220
|
+
prevReviewModeRubric.current = reviewModeRubric;
|
|
25221
|
+
}, [apiOptions, choices, isLastUsedWidget, reviewModeRubric]); // When a particular choice's `onChange` handler is called, indicating a
|
|
25425
25222
|
// change in a single choice's values, we need to call our `onChange`
|
|
25426
25223
|
// handler in order to notify our parent. However, our API with our parent
|
|
25427
25224
|
// is that we always provide *all* values for *all* choices, even if just
|
|
@@ -25430,181 +25227,196 @@ class BaseRadio extends React__namespace.Component {
|
|
|
25430
25227
|
// will usually cause the old answer to become unchecked.)
|
|
25431
25228
|
//
|
|
25432
25229
|
// So, given the new values for a particular choice, compute the new values
|
|
25433
|
-
// for all choices, and pass them to `
|
|
25230
|
+
// for all choices, and pass them to `onChange`.
|
|
25434
25231
|
//
|
|
25435
25232
|
// `newValues` is an object with two keys: `checked` and `crossedOut`. Each
|
|
25436
25233
|
// contains a boolean value specifying the new checked and crossed-out
|
|
25437
25234
|
// value of this choice.
|
|
25438
25235
|
|
|
25236
|
+
function updateChoice(choiceIndex, newValues) {
|
|
25237
|
+
const {
|
|
25238
|
+
multipleSelect,
|
|
25239
|
+
choices,
|
|
25240
|
+
onChange
|
|
25241
|
+
} = props; // Get the baseline `checked` values. If we're checking a new answer
|
|
25242
|
+
// and multiple-select is not on, we should clear all choices to be
|
|
25243
|
+
// unchecked. Otherwise, we should copy the old checked values.
|
|
25439
25244
|
|
|
25440
|
-
|
|
25441
|
-
const inputType = this.props.multipleSelect ? "checkbox" : "radio";
|
|
25442
|
-
const rubric = this.props.reviewModeRubric;
|
|
25443
|
-
const reviewMode = !!rubric;
|
|
25444
|
-
const styles = BaseRadio.styles;
|
|
25445
|
-
const sat = this.props.apiOptions.satStyling;
|
|
25446
|
-
const isMobile = this.props.apiOptions.isMobile;
|
|
25447
|
-
const choices = this.props.choices;
|
|
25448
|
-
const firstChoiceHighlighted = choices[0].highlighted;
|
|
25449
|
-
const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
|
|
25450
|
-
const className = classNames__default["default"]("perseus-widget-radio", !this.props.editMode && "perseus-rendered-radio", aphrodite.css(styles.radio, // SAT doesn't use the "responsive styling" as it conflicts
|
|
25451
|
-
// with their custom theming.
|
|
25452
|
-
!sat && styles.responsiveRadioContainer, !sat && firstChoiceHighlighted && isMobile && styles.radioContainerFirstHighlighted, !sat && lastChoiceHighlighted && isMobile && styles.radioContainerLastHighlighted, sat && styles.satRadio));
|
|
25453
|
-
const instructionsClassName = classNames__default["default"]("instructions", aphrodite.css(styles.instructions, isMobile && styles.instructionsMobile));
|
|
25454
|
-
const instructions = this.getInstructionsText();
|
|
25455
|
-
const shouldShowInstructions = !sat;
|
|
25456
|
-
const responsiveClassName = aphrodite.css(styles.responsiveFieldset);
|
|
25457
|
-
const fieldset = /*#__PURE__*/React__namespace.createElement("fieldset", {
|
|
25458
|
-
className: "perseus-widget-radio-fieldset ".concat(responsiveClassName)
|
|
25459
|
-
}, /*#__PURE__*/React__namespace.createElement("legend", {
|
|
25460
|
-
className: "perseus-sr-only"
|
|
25461
|
-
}, instructions), shouldShowInstructions && /*#__PURE__*/React__namespace.createElement("div", {
|
|
25462
|
-
className: instructionsClassName
|
|
25463
|
-
}, instructions), /*#__PURE__*/React__namespace.createElement("ul", {
|
|
25464
|
-
className: className,
|
|
25465
|
-
style: {
|
|
25466
|
-
listStyle: "none"
|
|
25467
|
-
}
|
|
25468
|
-
}, this.props.choices.map(function (choice, i) {
|
|
25469
|
-
let Element = Choice;
|
|
25470
|
-
const elementProps = {
|
|
25471
|
-
ref: "radio".concat(i),
|
|
25472
|
-
apiOptions: this.props.apiOptions,
|
|
25473
|
-
checked: choice.checked,
|
|
25474
|
-
crossedOut: choice.crossedOut,
|
|
25475
|
-
previouslyAnswered: choice.previouslyAnswered,
|
|
25476
|
-
reviewMode,
|
|
25477
|
-
correct: choice.correct,
|
|
25478
|
-
rationale: choice.rationale,
|
|
25479
|
-
content: choice.content,
|
|
25480
|
-
disabled: this.props.apiOptions.readOnly || choice.disabled,
|
|
25481
|
-
editMode: this.props.editMode,
|
|
25482
|
-
groupName: this.state.radioGroupName,
|
|
25483
|
-
isLastChoice: i === this.props.choices.length - 1,
|
|
25484
|
-
showCorrectness: reviewMode || !!choice.showCorrectness,
|
|
25485
|
-
showRationale: choice.hasRationale && (reviewMode || choice.showRationale),
|
|
25486
|
-
type: inputType,
|
|
25487
|
-
pos: i,
|
|
25488
|
-
deselectEnabled: this.deselectEnabled(),
|
|
25489
|
-
onChange: newValues => {
|
|
25490
|
-
this.updateChoice(i, newValues);
|
|
25491
|
-
},
|
|
25492
|
-
goToPrevChoice: () => this.goToChoice(i - 1),
|
|
25493
|
-
goToNextChoice: () => this.goToChoice(i + 1)
|
|
25494
|
-
};
|
|
25245
|
+
let newCheckedList;
|
|
25495
25246
|
|
|
25496
|
-
|
|
25497
|
-
|
|
25247
|
+
if (newValues.checked && !multipleSelect) {
|
|
25248
|
+
newCheckedList = choices.map(_ => false);
|
|
25249
|
+
} else {
|
|
25250
|
+
newCheckedList = choices.map(c => c.checked);
|
|
25251
|
+
} // Get the baseline `crossedOut` values.
|
|
25498
25252
|
|
|
25499
|
-
___default["default"].extend(elementProps, {
|
|
25500
|
-
showContent: choice.revealNoneOfTheAbove
|
|
25501
|
-
});
|
|
25502
|
-
}
|
|
25503
25253
|
|
|
25504
|
-
|
|
25505
|
-
|
|
25506
|
-
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25512
|
-
|
|
25513
|
-
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
|
|
25517
|
-
|
|
25518
|
-
|
|
25519
|
-
|
|
25520
|
-
|
|
25521
|
-
|
|
25522
|
-
|
|
25523
|
-
|
|
25524
|
-
|
|
25525
|
-
|
|
25526
|
-
|
|
25527
|
-
|
|
25528
|
-
|
|
25529
|
-
|
|
25530
|
-
|
|
25531
|
-
|
|
25532
|
-
|
|
25533
|
-
|
|
25534
|
-
|
|
25535
|
-
|
|
25536
|
-
|
|
25537
|
-
|
|
25538
|
-
|
|
25539
|
-
|
|
25540
|
-
|
|
25541
|
-
|
|
25542
|
-
|
|
25543
|
-
|
|
25544
|
-
|
|
25545
|
-
|
|
25254
|
+
const newCrossedOutList = choices.map(c => c.crossedOut); // Update this choice's `checked` and `crossedOut` values.
|
|
25255
|
+
|
|
25256
|
+
newCheckedList[choiceIndex] = newValues.checked;
|
|
25257
|
+
newCrossedOutList[choiceIndex] = newValues.crossedOut;
|
|
25258
|
+
onChange({
|
|
25259
|
+
checked: newCheckedList,
|
|
25260
|
+
crossedOut: newCrossedOutList
|
|
25261
|
+
});
|
|
25262
|
+
} // register a callback with the parent that allows
|
|
25263
|
+
// the parent to focus an individual choice
|
|
25264
|
+
|
|
25265
|
+
|
|
25266
|
+
registerFocusFunction === null || registerFocusFunction === void 0 ? void 0 : registerFocusFunction(choiceIndex => {
|
|
25267
|
+
const ref = choiceRefs.current[choiceIndex || 0]; // note(matthew): we know this is only getting passed
|
|
25268
|
+
// to a WB Clickable button, so we force it to be of
|
|
25269
|
+
// type HTMLButtonElement
|
|
25270
|
+
|
|
25271
|
+
const anyNode = ReactDOM__default["default"].findDOMNode(ref.current);
|
|
25272
|
+
const buttonNode = anyNode;
|
|
25273
|
+
|
|
25274
|
+
if (buttonNode) {
|
|
25275
|
+
buttonNode.focus();
|
|
25276
|
+
} else {
|
|
25277
|
+
return false;
|
|
25278
|
+
}
|
|
25279
|
+
|
|
25280
|
+
return true;
|
|
25281
|
+
}); // some commonly used shorthands
|
|
25282
|
+
|
|
25283
|
+
const reviewMode = !!reviewModeRubric;
|
|
25284
|
+
const sat = apiOptions.satStyling;
|
|
25285
|
+
const isMobile = apiOptions.isMobile;
|
|
25286
|
+
const firstChoiceHighlighted = choices[0].highlighted;
|
|
25287
|
+
const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
|
|
25288
|
+
const className = classNames__default["default"]("perseus-widget-radio", !editMode && "perseus-rendered-radio", aphrodite.css(styles$r.radio, // SAT doesn't use the "responsive styling" as it conflicts
|
|
25289
|
+
// with their custom theming.
|
|
25290
|
+
!sat && styles$r.responsiveRadioContainer, !sat && firstChoiceHighlighted && isMobile && styles$r.radioContainerFirstHighlighted, !sat && lastChoiceHighlighted && isMobile && styles$r.radioContainerLastHighlighted, sat && styles$r.satRadio));
|
|
25291
|
+
const instructionsClassName = classNames__default["default"]("instructions", aphrodite.css(styles$r.instructions, isMobile && styles$r.instructionsMobile));
|
|
25292
|
+
const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect);
|
|
25293
|
+
const shouldShowInstructions = !sat;
|
|
25294
|
+
const responsiveClassName = aphrodite.css(styles$r.responsiveFieldset);
|
|
25295
|
+
const fieldset = /*#__PURE__*/React__namespace.createElement("fieldset", {
|
|
25296
|
+
className: "perseus-widget-radio-fieldset ".concat(responsiveClassName)
|
|
25297
|
+
}, /*#__PURE__*/React__namespace.createElement("legend", {
|
|
25298
|
+
className: "perseus-sr-only"
|
|
25299
|
+
}, instructions), shouldShowInstructions && /*#__PURE__*/React__namespace.createElement("div", {
|
|
25300
|
+
className: instructionsClassName
|
|
25301
|
+
}, instructions), /*#__PURE__*/React__namespace.createElement("ul", {
|
|
25302
|
+
className: className,
|
|
25303
|
+
style: {
|
|
25304
|
+
listStyle: "none"
|
|
25305
|
+
}
|
|
25306
|
+
}, choices.map(function (choice, i) {
|
|
25307
|
+
let Element = Choice$1;
|
|
25308
|
+
const ref = /*#__PURE__*/React__namespace.createRef();
|
|
25309
|
+
choiceRefs.current[i] = ref;
|
|
25310
|
+
const elementProps = {
|
|
25311
|
+
apiOptions: apiOptions,
|
|
25312
|
+
multipleSelect: multipleSelect,
|
|
25313
|
+
checked: choice.checked,
|
|
25314
|
+
crossedOut: choice.crossedOut,
|
|
25315
|
+
previouslyAnswered: choice.previouslyAnswered,
|
|
25316
|
+
reviewMode,
|
|
25317
|
+
correct: choice.correct,
|
|
25318
|
+
rationale: choice.rationale,
|
|
25319
|
+
content: choice.content,
|
|
25320
|
+
disabled: apiOptions.readOnly || choice.disabled,
|
|
25321
|
+
showCorrectness: reviewMode || !!choice.showCorrectness,
|
|
25322
|
+
showRationale: choice.hasRationale && (reviewMode || choice.showRationale),
|
|
25323
|
+
pos: i,
|
|
25324
|
+
onChange: newValues => {
|
|
25325
|
+
updateChoice(i, newValues);
|
|
25326
|
+
},
|
|
25327
|
+
ref
|
|
25328
|
+
};
|
|
25546
25329
|
|
|
25547
|
-
|
|
25330
|
+
if (choice.isNoneOfTheAbove) {
|
|
25331
|
+
Element = ChoiceNoneAbove$1;
|
|
25332
|
+
|
|
25333
|
+
___default["default"].extend(elementProps, {
|
|
25334
|
+
showContent: choice.revealNoneOfTheAbove
|
|
25335
|
+
});
|
|
25336
|
+
}
|
|
25337
|
+
|
|
25338
|
+
const nextChoice = choices[i + 1];
|
|
25339
|
+
const nextChoiceHighlighted = !!nextChoice && nextChoice.highlighted;
|
|
25340
|
+
|
|
25341
|
+
const aphroditeClassName = checked => {
|
|
25342
|
+
// Whether or not to show correctness borders
|
|
25343
|
+
// for this choice and the next choice.
|
|
25344
|
+
const satShowCorrectness = sat && reviewMode && checked;
|
|
25345
|
+
const satShowCorrectnessNext = sat && reviewMode && nextChoice && nextChoice.checked;
|
|
25346
|
+
return aphrodite.css(sharedStyles.aboveScratchpad, styles$r.item, !sat && styles$r.responsiveItem, !sat && checked && styles$r.selectedItem, !sat && checked && choice.highlighted && styles$r.aboveBackdrop, !sat && checked && choice.highlighted && apiOptions.isMobile && styles$r.aboveBackdropMobile, !sat && nextChoiceHighlighted && apiOptions.isMobile && styles$r.nextHighlighted, sat && styles$r.satRadioOption, satShowCorrectness && !choice.correct && styles$r.satRadioOptionIncorrect, satShowCorrectness && choice.correct && styles$r.satRadioOptionCorrect, satShowCorrectnessNext && !nextChoice.correct && styles$r.satRadioOptionNextIncorrect, satShowCorrectnessNext && nextChoice.correct && styles$r.satRadioOptionNextCorrect, sat && reviewModeRubric && styles$r.satReviewRadioOption);
|
|
25347
|
+
}; // HACK(abdulrahman): Preloads the selection-state
|
|
25348
|
+
// css because of a bug that causes iOS to lag
|
|
25349
|
+
// when selecting the button for the first time.
|
|
25350
|
+
|
|
25351
|
+
|
|
25352
|
+
aphroditeClassName(true);
|
|
25353
|
+
let correctnessClass; // reviewMode is only true if there's a rubric
|
|
25354
|
+
// but Flow doesn't understand that
|
|
25355
|
+
|
|
25356
|
+
if (reviewMode && reviewModeRubric) {
|
|
25357
|
+
correctnessClass = reviewModeRubric.choices[i].correct ? ClassNames.CORRECT : ClassNames.INCORRECT;
|
|
25358
|
+
}
|
|
25359
|
+
|
|
25360
|
+
const className = classNames__default["default"](aphroditeClassName(choice.checked), // TODO(aria): Make test case for these API
|
|
25361
|
+
// classNames
|
|
25362
|
+
ClassNames.RADIO.OPTION, choice.checked && ClassNames.RADIO.SELECTED, correctnessClass); // In edit mode, the Choice renders a Div in order to
|
|
25363
|
+
// allow for the contentEditable area to be selected
|
|
25364
|
+
// (label forces any clicks inside to select the input
|
|
25365
|
+
// element) We have to add some extra behavior to make
|
|
25366
|
+
// sure that we can still check the choice.
|
|
25367
|
+
|
|
25368
|
+
let listElem = null;
|
|
25369
|
+
let clickHandler = null;
|
|
25370
|
+
|
|
25371
|
+
if (editMode) {
|
|
25372
|
+
clickHandler = e => {
|
|
25373
|
+
// Traverse the parent nodes of the clicked
|
|
25374
|
+
// element.
|
|
25375
|
+
let elem = e.target;
|
|
25376
|
+
|
|
25377
|
+
while (elem && elem !== listElem) {
|
|
25378
|
+
// If the clicked element is inside of the
|
|
25379
|
+
// radio icon, then we want to trigger the
|
|
25380
|
+
// check by flipping the choice of the icon.
|
|
25381
|
+
if (elem.getAttribute("data-is-radio-icon")) {
|
|
25382
|
+
this.updateChoice(i, {
|
|
25383
|
+
checked: !choice.checked,
|
|
25384
|
+
crossedOut: choice.crossedOut
|
|
25385
|
+
});
|
|
25386
|
+
return;
|
|
25548
25387
|
}
|
|
25549
|
-
};
|
|
25550
|
-
} // TODO(mattdr): Index isn't a *good* choice of key
|
|
25551
|
-
// here; is there a better one? Can we use choice
|
|
25552
|
-
// content somehow? Would changing our choice of key
|
|
25553
|
-
// somehow break something happening inside a choice's
|
|
25554
|
-
// child Renderers, by changing when we mount/unmount?
|
|
25555
25388
|
|
|
25389
|
+
elem = elem.parentNode;
|
|
25390
|
+
}
|
|
25391
|
+
};
|
|
25392
|
+
} // TODO(mattdr): Index isn't a *good* choice of key
|
|
25393
|
+
// here; is there a better one? Can we use choice
|
|
25394
|
+
// content somehow? Would changing our choice of key
|
|
25395
|
+
// somehow break something happening inside a choice's
|
|
25396
|
+
// child Renderers, by changing when we mount/unmount?
|
|
25556
25397
|
|
|
25557
|
-
return /*#__PURE__*/React__namespace.createElement("li", {
|
|
25558
|
-
key: i,
|
|
25559
|
-
ref: e => listElem = e,
|
|
25560
|
-
className: className,
|
|
25561
|
-
onClick: clickHandler,
|
|
25562
|
-
onTouchStart: !this.props.labelWrap ? null : captureScratchpadTouchStart$2
|
|
25563
|
-
}, /*#__PURE__*/React__namespace.createElement(Element, elementProps));
|
|
25564
|
-
}, this))); // Allow for horizontal scrolling if content is too wide, which may be
|
|
25565
|
-
// an issue especially on phones.
|
|
25566
|
-
// This is disabled in SAT, since it conflicts with their theming.
|
|
25567
25398
|
|
|
25568
|
-
return /*#__PURE__*/React__namespace.createElement("
|
|
25569
|
-
|
|
25570
|
-
|
|
25571
|
-
|
|
25399
|
+
return /*#__PURE__*/React__namespace.createElement("li", {
|
|
25400
|
+
key: i,
|
|
25401
|
+
ref: e => listElem = e,
|
|
25402
|
+
className: className,
|
|
25403
|
+
onClick: clickHandler,
|
|
25404
|
+
onTouchStart: !labelWrap ? null : captureScratchpadTouchStart$2
|
|
25405
|
+
}, /*#__PURE__*/React__namespace.createElement(Element, elementProps));
|
|
25406
|
+
}, this))); // Allow for horizontal scrolling if content is too wide, which may be
|
|
25407
|
+
// an issue especially on phones.
|
|
25408
|
+
// This is disabled in SAT, since it conflicts with their theming.
|
|
25572
25409
|
|
|
25410
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
25411
|
+
className: aphrodite.css(!sat && styles$r.responsiveContainer)
|
|
25412
|
+
}, fieldset);
|
|
25573
25413
|
}
|
|
25574
25414
|
|
|
25575
|
-
|
|
25576
|
-
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
styling: PropTypes__default["default"].shape({
|
|
25581
|
-
radioStyleVersion: PropTypes__default["default"].oneOf(["intermediate", "final"])
|
|
25582
|
-
}),
|
|
25583
|
-
canScrollPage: PropTypes__default["default"].bool
|
|
25584
|
-
}),
|
|
25585
|
-
choices: ChoicesType,
|
|
25586
|
-
deselectEnabled: PropTypes__default["default"].bool,
|
|
25587
|
-
editMode: PropTypes__default["default"].bool,
|
|
25588
|
-
labelWrap: PropTypes__default["default"].bool,
|
|
25589
|
-
countChoices: PropTypes__default["default"].bool,
|
|
25590
|
-
numCorrect: PropTypes__default["default"].number,
|
|
25591
|
-
multipleSelect: PropTypes__default["default"].bool,
|
|
25592
|
-
reviewModeRubric: PropTypes__default["default"].shape({
|
|
25593
|
-
choices: ChoicesType
|
|
25594
|
-
}),
|
|
25595
|
-
// A callback indicating that this choice has changed. Its argument is
|
|
25596
|
-
// an object with two keys: `checked` and `crossedOut`. Each contains
|
|
25597
|
-
// an array of boolean values, specifying the new checked and
|
|
25598
|
-
// crossed-out value of each choice.
|
|
25599
|
-
onChange: PropTypes__default["default"].func,
|
|
25600
|
-
// Whether this widget was the most recently used widget in this
|
|
25601
|
-
// Renderer. Determines whether we'll auto-scroll the page upon
|
|
25602
|
-
// entering review mode.
|
|
25603
|
-
isLastUsedWidget: PropTypes__default["default"].bool
|
|
25604
|
-
});
|
|
25605
|
-
|
|
25606
|
-
_defineProperty(BaseRadio, "styles", aphrodite.StyleSheet.create({
|
|
25607
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25415
|
+
BaseRadio.defaultProps = {
|
|
25416
|
+
editMode: false,
|
|
25417
|
+
multipleSelect: false
|
|
25418
|
+
};
|
|
25419
|
+
const styles$r = aphrodite.StyleSheet.create({
|
|
25608
25420
|
instructions: {
|
|
25609
25421
|
display: "block",
|
|
25610
25422
|
color: gray17,
|
|
@@ -25614,7 +25426,6 @@ _defineProperty(BaseRadio, "styles", aphrodite.StyleSheet.create({
|
|
|
25614
25426
|
fontWeight: "bold",
|
|
25615
25427
|
marginBottom: 16
|
|
25616
25428
|
},
|
|
25617
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25618
25429
|
instructionsMobile: {
|
|
25619
25430
|
fontSize: 18,
|
|
25620
25431
|
[mediaQueries.smOrSmaller]: {
|
|
@@ -25628,36 +25439,30 @@ _defineProperty(BaseRadio, "styles", aphrodite.StyleSheet.create({
|
|
|
25628
25439
|
fontSize: 20
|
|
25629
25440
|
}
|
|
25630
25441
|
},
|
|
25631
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25632
25442
|
radio: {
|
|
25633
25443
|
// Avoid centering
|
|
25634
25444
|
width: "100%"
|
|
25635
25445
|
},
|
|
25636
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25637
25446
|
responsiveRadioContainer: {
|
|
25638
|
-
borderBottom: "1px solid ".concat(radioBorderColor),
|
|
25639
|
-
borderTop: "1px solid ".concat(radioBorderColor),
|
|
25447
|
+
borderBottom: "1px solid ".concat(radioBorderColor$1),
|
|
25448
|
+
borderTop: "1px solid ".concat(radioBorderColor$1),
|
|
25640
25449
|
width: "auto",
|
|
25641
25450
|
[mediaQueries.smOrSmaller]: {
|
|
25642
25451
|
marginLeft: negativePhoneMargin,
|
|
25643
25452
|
marginRight: negativePhoneMargin
|
|
25644
25453
|
}
|
|
25645
25454
|
},
|
|
25646
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25647
25455
|
radioContainerFirstHighlighted: {
|
|
25648
25456
|
borderTop: "1px solid rgba(0, 0, 0, 0)"
|
|
25649
25457
|
},
|
|
25650
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25651
25458
|
radioContainerLastHighlighted: {
|
|
25652
25459
|
borderBottom: "1px solid rgba(0, 0, 0, 0)"
|
|
25653
25460
|
},
|
|
25654
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25655
25461
|
satRadio: {
|
|
25656
25462
|
background: "none",
|
|
25657
25463
|
marginLeft: 0,
|
|
25658
25464
|
userSelect: "none"
|
|
25659
25465
|
},
|
|
25660
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25661
25466
|
satRadioOption: {
|
|
25662
25467
|
margin: 0,
|
|
25663
25468
|
padding: 0,
|
|
@@ -25666,57 +25471,40 @@ _defineProperty(BaseRadio, "styles", aphrodite.StyleSheet.create({
|
|
|
25666
25471
|
borderTop: "1px solid #ccc"
|
|
25667
25472
|
}
|
|
25668
25473
|
},
|
|
25669
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25670
25474
|
satRadioOptionCorrect: {
|
|
25671
25475
|
borderBottomColor: satCorrectBorderColor,
|
|
25672
25476
|
":first-child": {
|
|
25673
25477
|
borderTopColor: satCorrectBorderColor
|
|
25674
25478
|
}
|
|
25675
25479
|
},
|
|
25676
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25677
25480
|
satRadioOptionIncorrect: {
|
|
25678
25481
|
borderBottomColor: satIncorrectBorderColor,
|
|
25679
25482
|
":first-child": {
|
|
25680
25483
|
borderTopColor: satIncorrectBorderColor
|
|
25681
25484
|
}
|
|
25682
25485
|
},
|
|
25683
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25684
25486
|
satRadioOptionNextCorrect: {
|
|
25685
25487
|
borderBottomColor: satCorrectBorderColor
|
|
25686
25488
|
},
|
|
25687
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25688
25489
|
satRadioOptionNextIncorrect: {
|
|
25689
25490
|
borderBottomColor: satIncorrectBorderColor
|
|
25690
25491
|
},
|
|
25691
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25692
25492
|
satReviewRadioOption: {
|
|
25693
25493
|
pointerEvents: "none"
|
|
25694
25494
|
},
|
|
25695
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25696
25495
|
item: {
|
|
25697
25496
|
marginLeft: 20
|
|
25698
25497
|
},
|
|
25699
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25700
|
-
inlineItem: {
|
|
25701
|
-
display: "inline-block",
|
|
25702
|
-
paddingLeft: 20,
|
|
25703
|
-
verticalAlign: "middle" // See http://stackoverflow.com/q/8120466 for explanation of
|
|
25704
|
-
// why vertical align property is needed
|
|
25705
|
-
|
|
25706
|
-
},
|
|
25707
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25708
25498
|
responsiveItem: {
|
|
25709
25499
|
marginLeft: 0,
|
|
25710
25500
|
padding: 0,
|
|
25711
25501
|
":not(:last-child)": {
|
|
25712
|
-
borderBottom: "1px solid ".concat(radioBorderColor)
|
|
25502
|
+
borderBottom: "1px solid ".concat(radioBorderColor$1)
|
|
25713
25503
|
}
|
|
25714
25504
|
},
|
|
25715
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25716
25505
|
selectedItem: {
|
|
25717
25506
|
background: "white"
|
|
25718
25507
|
},
|
|
25719
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25720
25508
|
aboveBackdrop: {
|
|
25721
25509
|
position: "relative",
|
|
25722
25510
|
// HACK(emily): We want selected choices to show up above our
|
|
@@ -25725,20 +25513,17 @@ _defineProperty(BaseRadio, "styles", aphrodite.StyleSheet.create({
|
|
|
25725
25513
|
// coordinated between here and webapp. :(
|
|
25726
25514
|
zIndex: 1062
|
|
25727
25515
|
},
|
|
25728
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25729
25516
|
aboveBackdropMobile: {
|
|
25730
25517
|
boxShadow: "0 0 4px 0 rgba(0, 0, 0, 0.2)," + "0 0 2px 0 rgba(0, 0, 0, 0.1)",
|
|
25731
25518
|
":not(:last-child)": {
|
|
25732
25519
|
borderBottom: "1px solid rgba(0, 0, 0, 0)"
|
|
25733
25520
|
}
|
|
25734
25521
|
},
|
|
25735
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25736
25522
|
nextHighlighted: {
|
|
25737
25523
|
":not(:last-child)": {
|
|
25738
25524
|
borderBottom: "1px solid rgba(0, 0, 0, 0)"
|
|
25739
25525
|
}
|
|
25740
25526
|
},
|
|
25741
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25742
25527
|
responsiveContainer: {
|
|
25743
25528
|
overflow: "auto",
|
|
25744
25529
|
marginLeft: negativePhoneMargin,
|
|
@@ -25746,22 +25531,22 @@ _defineProperty(BaseRadio, "styles", aphrodite.StyleSheet.create({
|
|
|
25746
25531
|
paddingLeft: phoneMargin // paddingRight is handled by responsiveFieldset
|
|
25747
25532
|
|
|
25748
25533
|
},
|
|
25749
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25750
25534
|
responsiveFieldset: {
|
|
25751
25535
|
paddingRight: phoneMargin
|
|
25752
25536
|
}
|
|
25753
|
-
}));
|
|
25754
|
-
|
|
25755
|
-
_defineProperty(BaseRadio, "defaultProps", {
|
|
25756
|
-
editMode: false
|
|
25757
25537
|
});
|
|
25758
25538
|
|
|
25759
25539
|
class Radio$1 extends React__namespace.Component {
|
|
25760
25540
|
constructor() {
|
|
25541
|
+
var _this;
|
|
25542
|
+
|
|
25761
25543
|
super(...arguments);
|
|
25544
|
+
_this = this;
|
|
25545
|
+
|
|
25546
|
+
_defineProperty(this, "focusFunction", void 0);
|
|
25762
25547
|
|
|
25763
|
-
_defineProperty(this, "_renderRenderer",
|
|
25764
|
-
content =
|
|
25548
|
+
_defineProperty(this, "_renderRenderer", function () {
|
|
25549
|
+
let content = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
|
|
25765
25550
|
let nextPassageRefId = 1;
|
|
25766
25551
|
const widgets = {};
|
|
25767
25552
|
const modContent = content.replace(/\{\{passage-ref (\d+) (\d+)(?: "([^"]*)")?\}\}/g, (match, passageNum, refNum, summaryText) => {
|
|
@@ -25793,19 +25578,14 @@ class Radio$1 extends React__namespace.Component {
|
|
|
25793
25578
|
key: "choiceContentRenderer",
|
|
25794
25579
|
content: modContent,
|
|
25795
25580
|
widgets: widgets,
|
|
25796
|
-
findExternalWidgets:
|
|
25581
|
+
findExternalWidgets: _this.props.findWidgets,
|
|
25797
25582
|
alwaysUpdate: true,
|
|
25798
|
-
linterContext: { ...
|
|
25583
|
+
linterContext: { ..._this.props.linterContext,
|
|
25799
25584
|
blockHighlight: true
|
|
25800
25585
|
}
|
|
25801
25586
|
});
|
|
25802
25587
|
});
|
|
25803
25588
|
|
|
25804
|
-
_defineProperty(this, "focus", i => {
|
|
25805
|
-
// eslint-disable-next-line react/no-string-refs
|
|
25806
|
-
return this.refs.baseRadio.focus(i);
|
|
25807
|
-
});
|
|
25808
|
-
|
|
25809
25589
|
_defineProperty(this, "updateChoices", newValueLists => {
|
|
25810
25590
|
const {
|
|
25811
25591
|
choiceStates,
|
|
@@ -25816,25 +25596,18 @@ class Radio$1 extends React__namespace.Component {
|
|
|
25816
25596
|
// should clone the old `choiceStates` objects, in preparation to
|
|
25817
25597
|
// mutate them.
|
|
25818
25598
|
|
|
25819
|
-
|
|
25820
|
-
|
|
25821
|
-
|
|
25822
|
-
|
|
25823
|
-
|
|
25824
|
-
|
|
25825
|
-
|
|
25826
|
-
|
|
25827
|
-
|
|
25828
|
-
|
|
25829
|
-
rationaleShown: false,
|
|
25830
|
-
correctnessShown: false,
|
|
25831
|
-
previouslyAnswered: false,
|
|
25832
|
-
readOnly: false
|
|
25833
|
-
}));
|
|
25834
|
-
} // Mutate the new `choiceState` objects, according to the new `checked`
|
|
25599
|
+
const newChoiceStates = choiceStates ? choiceStates.map(state => ({ ...state
|
|
25600
|
+
})) : choices.map(() => ({
|
|
25601
|
+
selected: false,
|
|
25602
|
+
crossedOut: false,
|
|
25603
|
+
highlighted: false,
|
|
25604
|
+
rationaleShown: false,
|
|
25605
|
+
correctnessShown: false,
|
|
25606
|
+
previouslyAnswered: false,
|
|
25607
|
+
readOnly: false
|
|
25608
|
+
})); // Mutate the new `choiceState` objects, according to the new `checked`
|
|
25835
25609
|
// and `crossedOut` values provided in `newValueLists`.
|
|
25836
25610
|
|
|
25837
|
-
|
|
25838
25611
|
newChoiceStates.forEach((choiceState, i) => {
|
|
25839
25612
|
choiceState.selected = newValueLists.checked[i];
|
|
25840
25613
|
choiceState.crossedOut = newValueLists.crossedOut[i];
|
|
@@ -25898,14 +25671,47 @@ class Radio$1 extends React__namespace.Component {
|
|
|
25898
25671
|
});
|
|
25899
25672
|
}
|
|
25900
25673
|
|
|
25674
|
+
// TODO(LP-10672): I think this might be unused right now. I can't find anywhere
|
|
25675
|
+
// that we pass a value to `.focus()` and it seems to have been used for
|
|
25676
|
+
// adding hints when editing.
|
|
25677
|
+
// See: https://github.com/Khan/perseus/blame/e18582b4b69959270b90e237ef1813899711ddfa/src/widgets/radio.js#L169
|
|
25678
|
+
focus(choiceIndex) {
|
|
25679
|
+
if (this.focusFunction) {
|
|
25680
|
+
return this.focusFunction(choiceIndex);
|
|
25681
|
+
}
|
|
25682
|
+
|
|
25683
|
+
return false;
|
|
25684
|
+
} // lets BaseRadio regiser a focus callback so widget
|
|
25685
|
+
// can focus an individual choice
|
|
25686
|
+
|
|
25687
|
+
|
|
25688
|
+
registerFocusFunction(fun) {
|
|
25689
|
+
this.focusFunction = fun;
|
|
25690
|
+
} // When `BaseRadio`'s `onChange` handler is called, indicating a change in
|
|
25691
|
+
// our choices' state, we need to call our `onChange` handler in order to
|
|
25692
|
+
// persist those changes in the item's Perseus state.
|
|
25693
|
+
//
|
|
25694
|
+
// So, given the new values for each choice, construct the new
|
|
25695
|
+
// `choiceStates` objects, and pass them to `this.props.onChange`.
|
|
25696
|
+
//
|
|
25697
|
+
// `newValueLists` is an object with two keys: `checked` and `crossedOut`.
|
|
25698
|
+
// Each contains an array of boolean values, specifying the new checked and
|
|
25699
|
+
// crossed-out value of each choice.
|
|
25700
|
+
//
|
|
25701
|
+
// NOTE(mdr): This method expects to be auto-bound. If this component is
|
|
25702
|
+
// converted to an ES6 class, take care to auto-bind this method!
|
|
25703
|
+
|
|
25704
|
+
|
|
25901
25705
|
render() {
|
|
25902
|
-
|
|
25706
|
+
const {
|
|
25707
|
+
choices
|
|
25708
|
+
} = this.props;
|
|
25903
25709
|
let choiceStates;
|
|
25904
25710
|
|
|
25905
25711
|
if (this.props.static) {
|
|
25906
|
-
choiceStates =
|
|
25907
|
-
selected:
|
|
25908
|
-
crossedOut:
|
|
25712
|
+
choiceStates = choices.map(choice => ({
|
|
25713
|
+
selected: !!choice.correct,
|
|
25714
|
+
crossedOut: false,
|
|
25909
25715
|
readOnly: true,
|
|
25910
25716
|
highlighted: false,
|
|
25911
25717
|
rationaleShown: true,
|
|
@@ -25913,12 +25719,12 @@ class Radio$1 extends React__namespace.Component {
|
|
|
25913
25719
|
previouslyAnswered: false
|
|
25914
25720
|
}));
|
|
25915
25721
|
} else if (this.props.choiceStates) {
|
|
25916
|
-
choiceStates = this.props.choiceStates;
|
|
25722
|
+
choiceStates = this.props.choiceStates;
|
|
25917
25723
|
} else if (this.props.values) {
|
|
25918
25724
|
// Support legacy choiceStates implementation
|
|
25919
25725
|
|
|
25920
25726
|
/* istanbul ignore next - props.values is deprecated */
|
|
25921
|
-
choiceStates =
|
|
25727
|
+
choiceStates = this.props.values.map(val => ({
|
|
25922
25728
|
selected: val,
|
|
25923
25729
|
crossedOut: false,
|
|
25924
25730
|
readOnly: false,
|
|
@@ -25928,7 +25734,7 @@ class Radio$1 extends React__namespace.Component {
|
|
|
25928
25734
|
previouslyAnswered: false
|
|
25929
25735
|
}));
|
|
25930
25736
|
} else {
|
|
25931
|
-
choiceStates =
|
|
25737
|
+
choiceStates = choices.map(() => ({
|
|
25932
25738
|
selected: false,
|
|
25933
25739
|
crossedOut: false,
|
|
25934
25740
|
readOnly: false,
|
|
@@ -25939,7 +25745,7 @@ class Radio$1 extends React__namespace.Component {
|
|
|
25939
25745
|
}));
|
|
25940
25746
|
}
|
|
25941
25747
|
|
|
25942
|
-
|
|
25748
|
+
const choicesProp = choices.map((choice, i) => {
|
|
25943
25749
|
const content = choice.isNoneOfTheAbove && !choice.content ? // we use i18n._ instead of $_ here because the content
|
|
25944
25750
|
// sent to a renderer needs to be a string, not a react
|
|
25945
25751
|
// node (/renderable/fragment).
|
|
@@ -25967,37 +25773,36 @@ class Radio$1 extends React__namespace.Component {
|
|
|
25967
25773
|
// TODO(emily): Come up with a more comprehensive way to solve
|
|
25968
25774
|
// this sort of "serialized state breaks when internal
|
|
25969
25775
|
// structure changes" problem.
|
|
25970
|
-
correct:
|
|
25776
|
+
correct: choice.correct === undefined ? !!reviewChoice && !!reviewChoice.correct : choice.correct,
|
|
25971
25777
|
disabled: readOnly,
|
|
25972
25778
|
hasRationale: !!choice.clue,
|
|
25973
25779
|
rationale: this._renderRenderer(choice.clue),
|
|
25974
25780
|
showRationale: rationaleShown,
|
|
25975
25781
|
showCorrectness: correctnessShown,
|
|
25976
|
-
isNoneOfTheAbove: choice.isNoneOfTheAbove,
|
|
25977
|
-
revealNoneOfTheAbove: this.props.questionCompleted && selected,
|
|
25782
|
+
isNoneOfTheAbove: !!choice.isNoneOfTheAbove,
|
|
25783
|
+
revealNoneOfTheAbove: !!(this.props.questionCompleted && selected),
|
|
25978
25784
|
crossedOut,
|
|
25979
25785
|
highlighted,
|
|
25980
25786
|
previouslyAnswered: previouslyAnswered
|
|
25981
25787
|
};
|
|
25982
25788
|
});
|
|
25983
|
-
return /*#__PURE__*/React__namespace.createElement(BaseRadio
|
|
25984
|
-
, {
|
|
25985
|
-
ref: "baseRadio",
|
|
25789
|
+
return /*#__PURE__*/React__namespace.createElement(BaseRadio, {
|
|
25986
25790
|
labelWrap: true,
|
|
25987
25791
|
multipleSelect: this.props.multipleSelect,
|
|
25988
25792
|
countChoices: this.props.countChoices,
|
|
25989
25793
|
numCorrect: this.props.numCorrect,
|
|
25990
|
-
choices:
|
|
25794
|
+
choices: choicesProp,
|
|
25991
25795
|
onChange: this.updateChoices,
|
|
25992
25796
|
reviewModeRubric: this.props.reviewModeRubric,
|
|
25993
25797
|
deselectEnabled: this.props.deselectEnabled,
|
|
25994
25798
|
apiOptions: this.props.apiOptions,
|
|
25995
|
-
isLastUsedWidget: this.props.isLastUsedWidget
|
|
25799
|
+
isLastUsedWidget: this.props.isLastUsedWidget,
|
|
25800
|
+
registerFocusFunction: i => this.registerFocusFunction(i)
|
|
25996
25801
|
});
|
|
25997
25802
|
}
|
|
25998
25803
|
|
|
25999
25804
|
static validate(userInput, rubric) {
|
|
26000
|
-
const numSelected =
|
|
25805
|
+
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
26001
25806
|
return sum + (selected ? 1 : 0);
|
|
26002
25807
|
}, 0);
|
|
26003
25808
|
|
|
@@ -26006,10 +25811,9 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26006
25811
|
type: "invalid",
|
|
26007
25812
|
message: null
|
|
26008
25813
|
};
|
|
26009
|
-
}
|
|
26010
|
-
|
|
25814
|
+
}
|
|
26011
25815
|
|
|
26012
|
-
if (userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
25816
|
+
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
26013
25817
|
return {
|
|
26014
25818
|
type: "invalid",
|
|
26015
25819
|
message: i18n__namespace._("Please choose the correct number of answers.")
|
|
@@ -26022,14 +25826,12 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26022
25826
|
message: i18n__namespace._("'None of the above' may not be selected " + "when other answers are selected.")
|
|
26023
25827
|
};
|
|
26024
25828
|
}
|
|
26025
|
-
/* jshint -W018 */
|
|
26026
|
-
|
|
26027
25829
|
|
|
26028
|
-
const correct =
|
|
25830
|
+
const correct = userInput.choicesSelected.every((selected, i) => {
|
|
26029
25831
|
let isCorrect;
|
|
26030
25832
|
|
|
26031
25833
|
if (userInput.noneOfTheAboveIndex === i) {
|
|
26032
|
-
isCorrect =
|
|
25834
|
+
isCorrect = rubric.choices.every((choice, j) => {
|
|
26033
25835
|
return i === j || !choice.correct;
|
|
26034
25836
|
});
|
|
26035
25837
|
} else {
|
|
@@ -26038,9 +25840,6 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26038
25840
|
|
|
26039
25841
|
return isCorrect === selected;
|
|
26040
25842
|
});
|
|
26041
|
-
/* jshint +W018 */
|
|
26042
|
-
|
|
26043
|
-
|
|
26044
25843
|
return {
|
|
26045
25844
|
type: "points",
|
|
26046
25845
|
earned: correct ? 1 : 0,
|
|
@@ -26062,9 +25861,7 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26062
25861
|
const numCorrect = props.numCorrect;
|
|
26063
25862
|
|
|
26064
25863
|
for (let i = 0; i < choicesSelected.length; i++) {
|
|
26065
|
-
|
|
26066
|
-
const index = props.choices[i].originalIndex; // $FlowFixMe[incompatible-use]
|
|
26067
|
-
|
|
25864
|
+
const index = props.choices[i].originalIndex;
|
|
26068
25865
|
choicesSelected[index] = choiceStates[i].selected;
|
|
26069
25866
|
|
|
26070
25867
|
if (props.choices[i].isNoneOfTheAbove) {
|
|
@@ -26085,31 +25882,35 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26085
25882
|
}; // Support legacy choiceState implementation
|
|
26086
25883
|
}
|
|
26087
25884
|
/* istanbul ignore if - props.values is deprecated */
|
|
26088
|
-
// $FlowFixMe[prop-missing]
|
|
26089
25885
|
|
|
26090
25886
|
|
|
26091
|
-
|
|
25887
|
+
const {
|
|
25888
|
+
values
|
|
25889
|
+
} = props;
|
|
25890
|
+
|
|
25891
|
+
if (values) {
|
|
26092
25892
|
let noneOfTheAboveIndex = null;
|
|
26093
25893
|
let noneOfTheAboveSelected = false;
|
|
26094
|
-
const
|
|
25894
|
+
const choicesSelected = [...values];
|
|
26095
25895
|
const countChoices = props.countChoices;
|
|
26096
25896
|
const numCorrect = props.numCorrect;
|
|
25897
|
+
const valuesLength = values.length;
|
|
26097
25898
|
|
|
26098
|
-
for (let i = 0; i <
|
|
25899
|
+
for (let i = 0; i < valuesLength; i++) {
|
|
26099
25900
|
const index = props.choices[i].originalIndex;
|
|
26100
|
-
|
|
25901
|
+
choicesSelected[index] = values[i];
|
|
26101
25902
|
|
|
26102
25903
|
if (props.choices[i].isNoneOfTheAbove) {
|
|
26103
25904
|
noneOfTheAboveIndex = index;
|
|
26104
25905
|
|
|
26105
|
-
if (
|
|
25906
|
+
if (choicesSelected[i]) {
|
|
26106
25907
|
noneOfTheAboveSelected = true;
|
|
26107
25908
|
}
|
|
26108
25909
|
}
|
|
26109
25910
|
}
|
|
26110
25911
|
|
|
26111
25912
|
return {
|
|
26112
|
-
choicesSelected
|
|
25913
|
+
choicesSelected,
|
|
26113
25914
|
noneOfTheAboveIndex,
|
|
26114
25915
|
noneOfTheAboveSelected,
|
|
26115
25916
|
countChoices,
|
|
@@ -26119,15 +25920,14 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26119
25920
|
|
|
26120
25921
|
|
|
26121
25922
|
return {
|
|
26122
|
-
choicesSelected:
|
|
25923
|
+
choicesSelected: props.choices.map(() => false)
|
|
26123
25924
|
};
|
|
26124
25925
|
}
|
|
26125
25926
|
|
|
26126
25927
|
}
|
|
26127
25928
|
|
|
26128
25929
|
_defineProperty(Radio$1, "defaultProps", {
|
|
26129
|
-
choices: [
|
|
26130
|
-
displayCount: null,
|
|
25930
|
+
choices: [],
|
|
26131
25931
|
multipleSelect: false,
|
|
26132
25932
|
countChoices: false,
|
|
26133
25933
|
deselectEnabled: false,
|
|
@@ -26158,14 +25958,14 @@ const _choiceTransform = (widgetOptions, problemNum) => {
|
|
|
26158
25958
|
|
|
26159
25959
|
const _addNoneOfAbove = function (choices) {
|
|
26160
25960
|
let noneOfTheAbove = null;
|
|
26161
|
-
|
|
26162
|
-
const newChoices = ___default["default"].reject(choices, function (choice, index) {
|
|
25961
|
+
const newChoices = choices.filter((choice, index) => {
|
|
26163
25962
|
if (choice.isNoneOfTheAbove) {
|
|
26164
25963
|
noneOfTheAbove = choice;
|
|
26165
|
-
return
|
|
25964
|
+
return false;
|
|
26166
25965
|
}
|
|
26167
|
-
}); // Place the "None of the above" options last
|
|
26168
25966
|
|
|
25967
|
+
return true;
|
|
25968
|
+
}); // Place the "None of the above" options last
|
|
26169
25969
|
|
|
26170
25970
|
if (noneOfTheAbove) {
|
|
26171
25971
|
newChoices.push(noneOfTheAbove);
|
|
@@ -26185,12 +25985,11 @@ const _choiceTransform = (widgetOptions, problemNum) => {
|
|
|
26185
25985
|
}; // Add meta-information to choices
|
|
26186
25986
|
|
|
26187
25987
|
|
|
26188
|
-
|
|
26189
|
-
|
|
26190
|
-
return ___default["default"].extend({}, choice, {
|
|
25988
|
+
const choices = widgetOptions.choices.map((choice, i) => {
|
|
25989
|
+
return { ...choice,
|
|
26191
25990
|
originalIndex: i,
|
|
26192
25991
|
correct: Boolean(choice.correct)
|
|
26193
|
-
}
|
|
25992
|
+
};
|
|
26194
25993
|
}); // Apply all the transforms. Note that the order we call these is
|
|
26195
25994
|
// important!
|
|
26196
25995
|
// 3) finally add "None of the above" to the bottom
|
|
@@ -40994,43 +40793,23 @@ const styles$9 = aphrodite.StyleSheet.create({
|
|
|
40994
40793
|
}
|
|
40995
40794
|
});
|
|
40996
40795
|
|
|
40997
|
-
const getLineHeightForNode = (
|
|
40998
|
-
return $line2.offset().top - $line1.offset().top;
|
|
40796
|
+
const getLineHeightForNode = (line1, line2) => {
|
|
40797
|
+
return $__default["default"](line2).offset().top - $__default["default"](line1).offset().top;
|
|
40999
40798
|
};
|
|
41000
40799
|
|
|
41001
|
-
|
|
41002
|
-
|
|
41003
|
-
|
|
41004
|
-
|
|
41005
|
-
|
|
41006
|
-
|
|
41007
|
-
|
|
41008
|
-
|
|
41009
|
-
|
|
41010
|
-
|
|
41011
|
-
|
|
41012
|
-
|
|
41013
|
-
}
|
|
41014
|
-
const SQUARE_LABEL_STYLE = {
|
|
41015
|
-
display: "inline-block",
|
|
41016
|
-
color: "rgb(255, 255, 255)",
|
|
41017
|
-
backgroundColor: "rgb(90, 90, 90)",
|
|
41018
|
-
paddingLeft: 10,
|
|
41019
|
-
paddingRight: 10,
|
|
41020
|
-
userSelect: "none",
|
|
41021
|
-
WebkitUserSelect: "none"
|
|
41022
|
-
};
|
|
41023
|
-
const CIRCLE_LABEL_STYLE = {
|
|
41024
|
-
display: "inline-block",
|
|
41025
|
-
color: "rgb(255, 255, 255)",
|
|
41026
|
-
backgroundColor: "rgb(90, 90, 90)",
|
|
41027
|
-
userSelect: "none",
|
|
41028
|
-
WebkitUserSelect: "none",
|
|
41029
|
-
width: 22,
|
|
41030
|
-
height: 22,
|
|
41031
|
-
borderRadius: "50%",
|
|
41032
|
-
textAlign: "center"
|
|
41033
|
-
};
|
|
40800
|
+
function getInitialParseState() {
|
|
40801
|
+
return {
|
|
40802
|
+
currentRef: [],
|
|
40803
|
+
useRefs: true,
|
|
40804
|
+
lastRef: 0,
|
|
40805
|
+
firstSentenceRef: null,
|
|
40806
|
+
firstQuestionRef: null,
|
|
40807
|
+
lastFootnote: {
|
|
40808
|
+
id: 0,
|
|
40809
|
+
text: ""
|
|
40810
|
+
}
|
|
40811
|
+
};
|
|
40812
|
+
}
|
|
41034
40813
|
|
|
41035
40814
|
class RefStart extends React__namespace.Component {
|
|
41036
40815
|
constructor() {
|
|
@@ -41049,10 +40828,6 @@ class RefStart extends React__namespace.Component {
|
|
|
41049
40828
|
|
|
41050
40829
|
}
|
|
41051
40830
|
|
|
41052
|
-
_defineProperty(RefStart, "propTypes", {
|
|
41053
|
-
refContent: PropTypes__default["default"].node.isRequired
|
|
41054
|
-
});
|
|
41055
|
-
|
|
41056
40831
|
class RefEnd extends React__namespace.Component {
|
|
41057
40832
|
render() {
|
|
41058
40833
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
@@ -41063,20 +40838,11 @@ class RefEnd extends React__namespace.Component {
|
|
|
41063
40838
|
}
|
|
41064
40839
|
|
|
41065
40840
|
const rules = {
|
|
41066
|
-
// $FlowFixMe[prop-missing]
|
|
41067
|
-
// $FlowFixMe[incompatible-use]
|
|
41068
40841
|
newline: SimpleMarkdown__default["default"].defaultRules.newline,
|
|
41069
|
-
// $FlowFixMe[prop-missing]
|
|
41070
|
-
// $FlowFixMe[incompatible-use]
|
|
41071
40842
|
paragraph: SimpleMarkdown__default["default"].defaultRules.paragraph,
|
|
41072
|
-
// $FlowFixMe[prop-missing]
|
|
41073
|
-
// $FlowFixMe[incompatible-use]
|
|
41074
40843
|
escape: SimpleMarkdown__default["default"].defaultRules.escape,
|
|
41075
40844
|
passageFootnote: {
|
|
41076
|
-
// $FlowFixMe[prop-missing]
|
|
41077
|
-
// $FlowFixMe[incompatible-use]
|
|
41078
40845
|
order: SimpleMarkdown__default["default"].defaultRules.escape.order + 0.1,
|
|
41079
|
-
// $FlowFixMe[prop-missing]
|
|
41080
40846
|
match: SimpleMarkdown__default["default"].inlineRegex(/^\^/),
|
|
41081
40847
|
parse: (capture, parse, state) => {
|
|
41082
40848
|
// if no footnotes have been seen, we're id 1. otherwise,
|
|
@@ -41105,10 +40871,8 @@ const rules = {
|
|
|
41105
40871
|
}
|
|
41106
40872
|
},
|
|
41107
40873
|
refStart: {
|
|
41108
|
-
// $FlowFixMe[prop-missing]
|
|
41109
|
-
// $FlowFixMe[incompatible-use]
|
|
41110
40874
|
order: SimpleMarkdown__default["default"].defaultRules.escape.order + 0.2,
|
|
41111
|
-
match: function (source
|
|
40875
|
+
match: function (source) {
|
|
41112
40876
|
const capture = /^\{\{/.exec(source);
|
|
41113
40877
|
|
|
41114
40878
|
if (capture) {
|
|
@@ -41139,12 +40903,14 @@ const rules = {
|
|
|
41139
40903
|
}
|
|
41140
40904
|
|
|
41141
40905
|
closeIndex++;
|
|
41142
|
-
}
|
|
41143
|
-
|
|
41144
|
-
const refText = source.slice(2, closeIndex); // A "magic" capture that matches the opening {{
|
|
40906
|
+
} // A "magic" capture that matches the opening {{
|
|
41145
40907
|
// but captures the full ref text internally :D
|
|
40908
|
+
// unfortunately the "magic" makes Flow upset,
|
|
40909
|
+
// so there's this questionable workaround
|
|
41146
40910
|
|
|
41147
|
-
|
|
40911
|
+
|
|
40912
|
+
capture[1] = source.slice(2, closeIndex);
|
|
40913
|
+
return capture;
|
|
41148
40914
|
}
|
|
41149
40915
|
|
|
41150
40916
|
return null;
|
|
@@ -41167,33 +40933,34 @@ const rules = {
|
|
|
41167
40933
|
// to make those refs into react refs on the
|
|
41168
40934
|
// passage, for instance!
|
|
41169
40935
|
useRefs: false
|
|
41170
|
-
},
|
|
40936
|
+
}, getInitialParseState()));
|
|
41171
40937
|
return {
|
|
41172
40938
|
ref: ref,
|
|
41173
40939
|
refContent: refContent
|
|
41174
40940
|
};
|
|
41175
40941
|
},
|
|
41176
40942
|
react: (node, output, state) => {
|
|
41177
|
-
|
|
40943
|
+
const ref = node.ref;
|
|
40944
|
+
|
|
40945
|
+
if (ref == null) {
|
|
41178
40946
|
return null;
|
|
41179
40947
|
} // We don't pass state here because this is parsed
|
|
41180
40948
|
// and output out-of-band. We don't want to affect
|
|
41181
40949
|
// our state by the double-output here :).
|
|
41182
40950
|
|
|
41183
40951
|
|
|
41184
|
-
const refContent = output(node.refContent, {});
|
|
40952
|
+
const refContent = output(node.refContent, {}); // note(matthewc) the refs created here become the refs
|
|
40953
|
+
// pulled from `this.refs` in passage.jsx
|
|
40954
|
+
|
|
41185
40955
|
return /*#__PURE__*/React__namespace.createElement(RefStart, {
|
|
41186
|
-
ref: START_REF_PREFIX +
|
|
41187
|
-
key: START_REF_PREFIX +
|
|
40956
|
+
ref: START_REF_PREFIX + ref,
|
|
40957
|
+
key: START_REF_PREFIX + ref,
|
|
41188
40958
|
refContent: refContent
|
|
41189
40959
|
});
|
|
41190
40960
|
}
|
|
41191
40961
|
},
|
|
41192
40962
|
refEnd: {
|
|
41193
|
-
// $FlowFixMe[prop-missing]
|
|
41194
|
-
// $FlowFixMe[incompatible-use]
|
|
41195
40963
|
order: SimpleMarkdown__default["default"].defaultRules.escape.order + 0.3,
|
|
41196
|
-
// $FlowFixMe[prop-missing]
|
|
41197
40964
|
match: SimpleMarkdown__default["default"].inlineRegex(/^\}\}/),
|
|
41198
40965
|
parse: (capture, parse, state) => {
|
|
41199
40966
|
if (!state.useRefs) {
|
|
@@ -41209,6 +40976,8 @@ const rules = {
|
|
|
41209
40976
|
},
|
|
41210
40977
|
react: (node, output, state) => {
|
|
41211
40978
|
if (node.ref != null) {
|
|
40979
|
+
// note(matthewc) the refs created here become the refs
|
|
40980
|
+
// pulled from `this.refs` in passage.jsx
|
|
41212
40981
|
return /*#__PURE__*/React__namespace.createElement(RefEnd, {
|
|
41213
40982
|
ref: END_REF_PREFIX + node.ref,
|
|
41214
40983
|
key: END_REF_PREFIX + node.ref
|
|
@@ -41222,10 +40991,7 @@ const rules = {
|
|
|
41222
40991
|
}
|
|
41223
40992
|
},
|
|
41224
40993
|
squareLabel: {
|
|
41225
|
-
// $FlowFixMe[prop-missing]
|
|
41226
|
-
// $FlowFixMe[incompatible-use]
|
|
41227
40994
|
order: SimpleMarkdown__default["default"].defaultRules.escape.order + 0.4,
|
|
41228
|
-
// $FlowFixMe[prop-missing]
|
|
41229
40995
|
match: SimpleMarkdown__default["default"].inlineRegex(/^\[\[(\w+)\]\]( *)/),
|
|
41230
40996
|
parse: (capture, parse, state) => {
|
|
41231
40997
|
if (!state.firstQuestionRef) {
|
|
@@ -41254,10 +41020,7 @@ const rules = {
|
|
|
41254
41020
|
}
|
|
41255
41021
|
},
|
|
41256
41022
|
circleLabel: {
|
|
41257
|
-
// $FlowFixMe[prop-missing]
|
|
41258
|
-
// $FlowFixMe[incompatible-use]
|
|
41259
41023
|
order: SimpleMarkdown__default["default"].defaultRules.escape.order + 0.5,
|
|
41260
|
-
// $FlowFixMe[prop-missing]
|
|
41261
41024
|
match: SimpleMarkdown__default["default"].inlineRegex(/^\(\((\w+)\)\)( *)/),
|
|
41262
41025
|
parse: (capture, parse, state) => {
|
|
41263
41026
|
return {
|
|
@@ -41282,10 +41045,7 @@ const rules = {
|
|
|
41282
41045
|
}
|
|
41283
41046
|
},
|
|
41284
41047
|
squareBracketRef: {
|
|
41285
|
-
// $FlowFixMe[prop-missing]
|
|
41286
|
-
// $FlowFixMe[incompatible-use]
|
|
41287
41048
|
order: SimpleMarkdown__default["default"].defaultRules.escape.order + 0.6,
|
|
41288
|
-
// $FlowFixMe[prop-missing]
|
|
41289
41049
|
match: SimpleMarkdown__default["default"].inlineRegex(/^\[(\d+)\]( *)/),
|
|
41290
41050
|
parse: (capture, parse, state) => {
|
|
41291
41051
|
if (!state.firstSentenceRef) {
|
|
@@ -41311,10 +41071,7 @@ const rules = {
|
|
|
41311
41071
|
}
|
|
41312
41072
|
},
|
|
41313
41073
|
highlight: {
|
|
41314
|
-
// $FlowFixMe[prop-missing]
|
|
41315
|
-
// $FlowFixMe[incompatible-use]
|
|
41316
41074
|
order: SimpleMarkdown__default["default"].defaultRules.escape.order + 0.7,
|
|
41317
|
-
// $FlowFixMe[prop-missing]
|
|
41318
41075
|
match: SimpleMarkdown__default["default"].inlineRegex(/^{highlighting.start}(.+?){highlighting.end}/),
|
|
41319
41076
|
parse: (capture, parse, state) => {
|
|
41320
41077
|
return {
|
|
@@ -41329,10 +41086,7 @@ const rules = {
|
|
|
41329
41086
|
}
|
|
41330
41087
|
},
|
|
41331
41088
|
reviewHighlight: {
|
|
41332
|
-
// $FlowFixMe[prop-missing]
|
|
41333
|
-
// $FlowFixMe[incompatible-use]
|
|
41334
41089
|
order: SimpleMarkdown__default["default"].defaultRules.escape.order + 0.7,
|
|
41335
|
-
// $FlowFixMe[prop-missing]
|
|
41336
41090
|
match: SimpleMarkdown__default["default"].inlineRegex(/^{review-highlighting.start}(.+?){review-highlighting.end}/),
|
|
41337
41091
|
parse: (capture, parse, state) => {
|
|
41338
41092
|
return {
|
|
@@ -41346,21 +41100,11 @@ const rules = {
|
|
|
41346
41100
|
}, node.content)];
|
|
41347
41101
|
}
|
|
41348
41102
|
},
|
|
41349
|
-
// $FlowFixMe[prop-missing]
|
|
41350
|
-
// $FlowFixMe[incompatible-use]
|
|
41351
41103
|
strong: SimpleMarkdown__default["default"].defaultRules.strong,
|
|
41352
|
-
// $FlowFixMe[prop-missing]
|
|
41353
|
-
// $FlowFixMe[incompatible-use]
|
|
41354
41104
|
u: SimpleMarkdown__default["default"].defaultRules.u,
|
|
41355
|
-
// $FlowFixMe[prop-missing]
|
|
41356
|
-
// $FlowFixMe[incompatible-use]
|
|
41357
41105
|
em: SimpleMarkdown__default["default"].defaultRules.em,
|
|
41358
|
-
// $FlowFixMe[prop-missing]
|
|
41359
|
-
// $FlowFixMe[incompatible-use]
|
|
41360
41106
|
del: SimpleMarkdown__default["default"].defaultRules.del,
|
|
41361
|
-
text: {
|
|
41362
|
-
// $FlowFixMe[incompatible-use]
|
|
41363
|
-
...SimpleMarkdown__default["default"].defaultRules.text,
|
|
41107
|
+
text: { ...SimpleMarkdown__default["default"].defaultRules.text,
|
|
41364
41108
|
|
|
41365
41109
|
react(node, output, state) {
|
|
41366
41110
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
@@ -41370,32 +41114,54 @@ const rules = {
|
|
|
41370
41114
|
|
|
41371
41115
|
}
|
|
41372
41116
|
};
|
|
41373
|
-
const
|
|
41374
|
-
|
|
41375
|
-
|
|
41376
|
-
|
|
41377
|
-
|
|
41378
|
-
|
|
41379
|
-
|
|
41380
|
-
|
|
41381
|
-
|
|
41382
|
-
|
|
41117
|
+
const START_REF_PREFIX = "start-ref-";
|
|
41118
|
+
const END_REF_PREFIX = "end-ref-";
|
|
41119
|
+
const REF_STYLE = {
|
|
41120
|
+
display: "inline-block",
|
|
41121
|
+
width: 0,
|
|
41122
|
+
visibility: "hidden"
|
|
41123
|
+
};
|
|
41124
|
+
const LABEL_OUTER_STYLE = {
|
|
41125
|
+
// for some reason we need these to keep the nbsp from wrapping when the
|
|
41126
|
+
// inner circle/square is display: inline-block
|
|
41127
|
+
display: "inline",
|
|
41128
|
+
whiteSpace: "nowrap"
|
|
41129
|
+
};
|
|
41130
|
+
const SQUARE_LABEL_STYLE = {
|
|
41131
|
+
display: "inline-block",
|
|
41132
|
+
color: "rgb(255, 255, 255)",
|
|
41133
|
+
backgroundColor: "rgb(90, 90, 90)",
|
|
41134
|
+
paddingLeft: 10,
|
|
41135
|
+
paddingRight: 10,
|
|
41136
|
+
userSelect: "none",
|
|
41137
|
+
WebkitUserSelect: "none"
|
|
41138
|
+
};
|
|
41139
|
+
const CIRCLE_LABEL_STYLE = {
|
|
41140
|
+
display: "inline-block",
|
|
41141
|
+
color: "rgb(255, 255, 255)",
|
|
41142
|
+
backgroundColor: "rgb(90, 90, 90)",
|
|
41143
|
+
userSelect: "none",
|
|
41144
|
+
WebkitUserSelect: "none",
|
|
41145
|
+
width: 22,
|
|
41146
|
+
height: 22,
|
|
41147
|
+
borderRadius: "50%",
|
|
41148
|
+
textAlign: "center"
|
|
41149
|
+
};
|
|
41383
41150
|
const builtParser = SimpleMarkdown__default["default"].parserFor(rules);
|
|
41384
41151
|
|
|
41385
41152
|
const parse = (source, state) => {
|
|
41386
41153
|
state = state || {};
|
|
41387
41154
|
const paragraphedSource = source + "\n\n";
|
|
41388
|
-
return builtParser(paragraphedSource, ___default["default"].extend(state,
|
|
41155
|
+
return builtParser(paragraphedSource, ___default["default"].extend(state, getInitialParseState()));
|
|
41389
41156
|
};
|
|
41390
41157
|
|
|
41158
|
+
const output = SimpleMarkdown__default["default"].reactFor(SimpleMarkdown__default["default"].ruleOutput(rules, "react"));
|
|
41391
41159
|
var PassageMarkdown = {
|
|
41392
|
-
parse
|
|
41393
|
-
|
|
41394
|
-
|
|
41395
|
-
|
|
41396
|
-
|
|
41397
|
-
END_REF_PREFIX: END_REF_PREFIX,
|
|
41398
|
-
_rulesForTesting: rules
|
|
41160
|
+
parse,
|
|
41161
|
+
output,
|
|
41162
|
+
START_REF_PREFIX,
|
|
41163
|
+
END_REF_PREFIX,
|
|
41164
|
+
getInitialParseState
|
|
41399
41165
|
};
|
|
41400
41166
|
|
|
41401
41167
|
// A fake paragraph to measure the line height of the passage. In CSS we always
|
|
@@ -41408,9 +41174,9 @@ class LineHeightMeasurer extends React__namespace.Component {
|
|
|
41408
41174
|
|
|
41409
41175
|
_defineProperty(this, "_cachedLineHeight", void 0);
|
|
41410
41176
|
|
|
41411
|
-
_defineProperty(this, "
|
|
41177
|
+
_defineProperty(this, "body", void 0);
|
|
41412
41178
|
|
|
41413
|
-
_defineProperty(this, "
|
|
41179
|
+
_defineProperty(this, "end", void 0);
|
|
41414
41180
|
}
|
|
41415
41181
|
|
|
41416
41182
|
measureLineHeight() {
|
|
@@ -41422,23 +41188,30 @@ class LineHeightMeasurer extends React__namespace.Component {
|
|
|
41422
41188
|
}
|
|
41423
41189
|
|
|
41424
41190
|
forceMeasureLineHeight() {
|
|
41425
|
-
|
|
41426
|
-
this
|
|
41191
|
+
const body = this.body;
|
|
41192
|
+
const end = this.end;
|
|
41193
|
+
|
|
41194
|
+
if (!body || !end) {
|
|
41195
|
+
return;
|
|
41196
|
+
} // Add some text which magically fills an entire line.
|
|
41197
|
+
|
|
41198
|
+
|
|
41199
|
+
body.textContent = " \u0080"; // Now, the line height is the difference between the top of the
|
|
41427
41200
|
// second line and the top of the first line.
|
|
41428
41201
|
|
|
41429
|
-
this._cachedLineHeight = getLineHeightForNode(
|
|
41202
|
+
this._cachedLineHeight = getLineHeightForNode(body, end); // Clear out the first line so it doesn't overlap the passage.
|
|
41430
41203
|
|
|
41431
|
-
|
|
41204
|
+
body.textContent = "";
|
|
41432
41205
|
}
|
|
41433
41206
|
|
|
41434
41207
|
render() {
|
|
41435
41208
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
41436
41209
|
className: aphrodite.css(styles$8.measurer)
|
|
41437
41210
|
}, /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("div", {
|
|
41438
|
-
ref:
|
|
41211
|
+
ref: ref => this.body = ref,
|
|
41439
41212
|
className: "paragraph"
|
|
41440
41213
|
}), /*#__PURE__*/React__namespace.createElement("div", {
|
|
41441
|
-
ref:
|
|
41214
|
+
ref: ref => this.end = ref
|
|
41442
41215
|
})));
|
|
41443
41216
|
}
|
|
41444
41217
|
|
|
@@ -41457,9 +41230,11 @@ class Passage extends React__namespace.Component {
|
|
|
41457
41230
|
constructor() {
|
|
41458
41231
|
super(...arguments);
|
|
41459
41232
|
|
|
41460
|
-
_defineProperty(this, "
|
|
41233
|
+
_defineProperty(this, "_contentRef", void 0);
|
|
41461
41234
|
|
|
41462
|
-
_defineProperty(this, "
|
|
41235
|
+
_defineProperty(this, "_lineHeightMeasurerRef", void 0);
|
|
41236
|
+
|
|
41237
|
+
_defineProperty(this, "_onResize", void 0);
|
|
41463
41238
|
|
|
41464
41239
|
_defineProperty(this, "_stylesAppiedTimer", void 0);
|
|
41465
41240
|
|
|
@@ -41480,6 +41255,8 @@ class Passage extends React__namespace.Component {
|
|
|
41480
41255
|
this._updateState();
|
|
41481
41256
|
|
|
41482
41257
|
this._onResize = ___default["default"].throttle(() => {
|
|
41258
|
+
var _this$_lineHeightMeas;
|
|
41259
|
+
|
|
41483
41260
|
// If we're rendering JIPT text, we won't have line numbers or a
|
|
41484
41261
|
// line height measurer, so skip handling this resize.
|
|
41485
41262
|
if (this.shouldRenderJipt()) {
|
|
@@ -41490,7 +41267,7 @@ class Passage extends React__namespace.Component {
|
|
|
41490
41267
|
// listen for resize events.
|
|
41491
41268
|
|
|
41492
41269
|
|
|
41493
|
-
this.
|
|
41270
|
+
(_this$_lineHeightMeas = this._lineHeightMeasurerRef) === null || _this$_lineHeightMeas === void 0 ? void 0 : _this$_lineHeightMeas.forceMeasureLineHeight();
|
|
41494
41271
|
|
|
41495
41272
|
this._updateState();
|
|
41496
41273
|
}, 500);
|
|
@@ -41553,9 +41330,8 @@ class Passage extends React__namespace.Component {
|
|
|
41553
41330
|
}
|
|
41554
41331
|
|
|
41555
41332
|
_measureLines() {
|
|
41556
|
-
|
|
41557
|
-
const
|
|
41558
|
-
const contentsHeight = $renderer.height();
|
|
41333
|
+
const renderer = ReactDOM__default["default"].findDOMNode(this._contentRef);
|
|
41334
|
+
const contentsHeight = $__default["default"](renderer).height();
|
|
41559
41335
|
|
|
41560
41336
|
const lineHeight = this._getLineHeight();
|
|
41561
41337
|
|
|
@@ -41582,7 +41358,9 @@ class Passage extends React__namespace.Component {
|
|
|
41582
41358
|
}
|
|
41583
41359
|
|
|
41584
41360
|
_getLineHeight() {
|
|
41585
|
-
|
|
41361
|
+
var _this$_lineHeightMeas2;
|
|
41362
|
+
|
|
41363
|
+
return ((_this$_lineHeightMeas2 = this._lineHeightMeasurerRef) === null || _this$_lineHeightMeas2 === void 0 ? void 0 : _this$_lineHeightMeas2.measureLineHeight()) || 0;
|
|
41586
41364
|
}
|
|
41587
41365
|
|
|
41588
41366
|
getLineCount() {
|
|
@@ -41598,12 +41376,16 @@ class Passage extends React__namespace.Component {
|
|
|
41598
41376
|
* These are functions to support the passage refs inter-widgets feature
|
|
41599
41377
|
* where other widgets can fetch the line numbers of a reference inside of
|
|
41600
41378
|
* a passage.
|
|
41379
|
+
*
|
|
41380
|
+
* todo(matthewc): The refs are created by PassageMarkdown's refStart and refEnd,
|
|
41381
|
+
* somehow bubbling up to Passage's `this.refs`. This runs against
|
|
41382
|
+
* current best practices for refs by using string refs, but also
|
|
41383
|
+
* by breaking our expectation of explicit data flow.
|
|
41601
41384
|
*/
|
|
41602
41385
|
|
|
41603
41386
|
|
|
41604
41387
|
_getStartRefLineNumber(referenceNumber) {
|
|
41605
|
-
const refRef = PassageMarkdown.START_REF_PREFIX + referenceNumber;
|
|
41606
|
-
|
|
41388
|
+
const refRef = PassageMarkdown.START_REF_PREFIX + referenceNumber;
|
|
41607
41389
|
const ref = this.refs[refRef];
|
|
41608
41390
|
|
|
41609
41391
|
if (!ref) {
|
|
@@ -41626,8 +41408,7 @@ class Passage extends React__namespace.Component {
|
|
|
41626
41408
|
}
|
|
41627
41409
|
|
|
41628
41410
|
_getEndRefLineNumber(referenceNumber) {
|
|
41629
|
-
const refRef = PassageMarkdown.END_REF_PREFIX + referenceNumber;
|
|
41630
|
-
|
|
41411
|
+
const refRef = PassageMarkdown.END_REF_PREFIX + referenceNumber;
|
|
41631
41412
|
const ref = this.refs[refRef];
|
|
41632
41413
|
|
|
41633
41414
|
if (!ref) {
|
|
@@ -41664,9 +41445,8 @@ class Passage extends React__namespace.Component {
|
|
|
41664
41445
|
}
|
|
41665
41446
|
|
|
41666
41447
|
_convertPosToLineNumber(absoluteVPos) {
|
|
41667
|
-
|
|
41668
|
-
const
|
|
41669
|
-
const relativeVPos = absoluteVPos - $content.offset().top;
|
|
41448
|
+
const content = ReactDOM__default["default"].findDOMNode(this._contentRef);
|
|
41449
|
+
const relativeVPos = absoluteVPos - $__default["default"](content).offset().top;
|
|
41670
41450
|
|
|
41671
41451
|
const lineHeight = this._getLineHeight();
|
|
41672
41452
|
|
|
@@ -41675,8 +41455,7 @@ class Passage extends React__namespace.Component {
|
|
|
41675
41455
|
}
|
|
41676
41456
|
|
|
41677
41457
|
_getRefContent(referenceNumber) {
|
|
41678
|
-
const refRef = PassageMarkdown.START_REF_PREFIX + referenceNumber;
|
|
41679
|
-
|
|
41458
|
+
const refRef = PassageMarkdown.START_REF_PREFIX + referenceNumber;
|
|
41680
41459
|
const ref = this.refs[refRef];
|
|
41681
41460
|
|
|
41682
41461
|
if (!ref) {
|
|
@@ -41782,12 +41561,9 @@ class Passage extends React__namespace.Component {
|
|
|
41782
41561
|
onSerializedHighlightsUpdate: this._handleSerializedHighlightsUpdate,
|
|
41783
41562
|
serializedHighlights: this.props.highlights
|
|
41784
41563
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
41785
|
-
ref:
|
|
41786
|
-
}, /*#__PURE__*/React__namespace.createElement(LineHeightMeasurer
|
|
41787
|
-
|
|
41788
|
-
// $FlowFixMe[incompatible-type](0.52.0->0.53.0)
|
|
41789
|
-
, {
|
|
41790
|
-
ref: e => this._lineHeightMeasurer = e
|
|
41564
|
+
ref: ref => this._contentRef = ref
|
|
41565
|
+
}, /*#__PURE__*/React__namespace.createElement(LineHeightMeasurer, {
|
|
41566
|
+
ref: ref => this._lineHeightMeasurerRef = ref
|
|
41791
41567
|
}), PassageMarkdown.output(parsed)));
|
|
41792
41568
|
}
|
|
41793
41569
|
|
|
@@ -41830,10 +41606,7 @@ class Passage extends React__namespace.Component {
|
|
|
41830
41606
|
});
|
|
41831
41607
|
}
|
|
41832
41608
|
|
|
41833
|
-
const parseState =
|
|
41834
|
-
firstSentenceRef: null,
|
|
41835
|
-
firstQuestionRef: null
|
|
41836
|
-
}; // Replace the vertical double quote characters quoting text with
|
|
41609
|
+
const parseState = PassageMarkdown.getInitialParseState(); // Replace the vertical double quote characters quoting text with
|
|
41837
41610
|
// an unicode left and right double quote characters. This would
|
|
41838
41611
|
// avoid rendering right or left unicode double quotes on both
|
|
41839
41612
|
// ends of a sentence wrapped by double quotes.
|