@khanacademy/perseus 0.4.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +6 -1
- package/CHANGELOG.md +45 -0
- package/dist/es/index.css +15 -8
- package/dist/es/index.js +1214 -1404
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +15 -8
- package/dist/index.js +1217 -1411
- package/dist/index.js.map +1 -1
- package/dist/strings.js +0 -4
- package/package.json +9 -8
- package/src/components/number-input.jsx +2 -2
- package/src/components/simple-keypad-input.jsx +18 -21
- package/src/index.js +1 -1
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +426 -135
- package/src/multi-items/__tests__/multi-renderer_test.js +9 -9
- package/src/perseus-api.jsx +10 -36
- package/src/perseus-types.js +18 -145
- package/src/styles/constants.js +0 -2
- package/src/styles/perseus-renderer.less +8 -2
- package/src/styles/variables.less +0 -2
- package/src/styles/widgets/passage.less +9 -5
- package/src/types.js +18 -35
- package/src/util/answer-types.js +2 -3
- package/src/util/math.js +3 -10
- package/src/util.js +0 -6
- package/src/version.js +1 -1
- package/src/widgets/__stories__/numeric-input.stories.jsx +43 -2
- package/src/widgets/__testdata__/base-radio_testdata.js +22 -0
- package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +4 -4
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +291 -130
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +3293 -1415
- package/src/widgets/__tests__/group_test.jsx +4 -4
- package/src/widgets/__tests__/numeric-input_test.jsx +12 -8
- package/src/widgets/__tests__/passage_test.jsx +81 -0
- package/src/widgets/__tests__/radio/base-radio_test.jsx +291 -41
- package/src/widgets/__tests__/radio/choice-icon_test.jsx +67 -0
- package/src/widgets/__tests__/radio/choice_test.jsx +302 -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/__tests__/radio_test.jsx +39 -15
- package/src/widgets/definition.jsx +1 -1
- package/src/widgets/group.jsx +84 -102
- package/src/widgets/input-number.jsx +6 -1
- package/src/widgets/numeric-input.jsx +87 -80
- 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 +485 -556
- package/src/widgets/radio/choice-a11y-text.js +62 -0
- package/src/widgets/radio/choice-icon/choice-icon.jsx +87 -0
- package/src/widgets/radio/choice-icon/cross-out-line.jsx +55 -0
- package/src/widgets/radio/choice-icon/library-choice-icon.jsx +214 -0
- package/src/widgets/radio/choice-icon/sat-choice-icon.jsx +120 -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 +319 -347
- 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/es/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { Rule, linterContextDefault, linterContextProps } from '@khanacademy/per
|
|
|
5
5
|
import * as i18n from '@khanacademy/wonder-blocks-i18n';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
import * as React from 'react';
|
|
8
|
+
import { useState, useEffect, useRef } from 'react';
|
|
8
9
|
import _, { debounce } from 'underscore';
|
|
9
10
|
import PropTypes from 'prop-types';
|
|
10
11
|
import $ from 'jquery';
|
|
@@ -31,7 +32,7 @@ import Link from '@khanacademy/wonder-blocks-link';
|
|
|
31
32
|
|
|
32
33
|
var version = {
|
|
33
34
|
apiVersion: {
|
|
34
|
-
major:
|
|
35
|
+
major: 12,
|
|
35
36
|
minor: 0
|
|
36
37
|
},
|
|
37
38
|
itemDataVersion: {
|
|
@@ -493,38 +494,9 @@ var ApiOptions = {
|
|
|
493
494
|
// values. For now, though, you must either specify all fields
|
|
494
495
|
// of `styling`, or omit the `styling` option entirely.
|
|
495
496
|
styling: PropTypes.shape({
|
|
496
|
-
//
|
|
497
|
-
//
|
|
498
|
-
|
|
499
|
-
// "legacy" was the version of the widget display after XOM but
|
|
500
|
-
// before we started adding MCR styles. It doesn't have support
|
|
501
|
-
// for rationales. It has since been removed.
|
|
502
|
-
//
|
|
503
|
-
// "intermediate" is a design which adds several new additions
|
|
504
|
-
// to the "legacy" styles such as:
|
|
505
|
-
// 1. Using the XOM "desktop" styles (with a visible check icon
|
|
506
|
-
// and lines in between choices) on mobile devices.
|
|
507
|
-
// 2. Designs for rationales
|
|
508
|
-
// 3. Using the single-select styles for multi-select styles
|
|
509
|
-
//
|
|
510
|
-
// "final" is a design which will build off of the
|
|
511
|
-
// "intermediate" designs and adds some improved designs as well
|
|
512
|
-
// as:
|
|
513
|
-
// 1. a/b/c/d/etc. letters inside of the prompt check box
|
|
514
|
-
// 2. New iconography and styles to indicate choice correctness
|
|
515
|
-
//
|
|
516
|
-
// The "legacy" and "intermediate" designs will be A/B tested
|
|
517
|
-
// against each other to ensure that its changes don't cause
|
|
518
|
-
// problems due to the new designs. Once the "intermediate"
|
|
519
|
-
// designs are finished, they will be A/B tested against the
|
|
520
|
-
// "final" designs.
|
|
521
|
-
//
|
|
522
|
-
// If no flag is provided, "legacy" styles will be shown.
|
|
523
|
-
//
|
|
524
|
-
// TODO(emily): Remove this by Aug 1, 2017, at which point all
|
|
525
|
-
// callsites should have been switched to using the "final"
|
|
526
|
-
// designs.
|
|
527
|
-
radioStyleVersion: PropTypes.oneOf(["intermediate", "final"])
|
|
497
|
+
// TODO(Nicole): Remove this after landing wonderblock-ifying
|
|
498
|
+
// the radio widget
|
|
499
|
+
primaryProductColor: PropTypes.string
|
|
528
500
|
}),
|
|
529
501
|
// The color used for the hint progress indicator (eg. 1 / 3)
|
|
530
502
|
hintProgressColor: PropTypes.string,
|
|
@@ -575,9 +547,7 @@ var ApiOptions = {
|
|
|
575
547
|
},
|
|
576
548
|
setDrawingAreaAvailable: function setDrawingAreaAvailable() {},
|
|
577
549
|
useDraftEditor: false,
|
|
578
|
-
styling: {
|
|
579
|
-
radioStyleVersion: "final"
|
|
580
|
-
},
|
|
550
|
+
styling: {},
|
|
581
551
|
canScrollPage: false,
|
|
582
552
|
inModal: false,
|
|
583
553
|
crossOutEnabled: false,
|
|
@@ -1506,8 +1476,7 @@ var KhanAnswerTypes = {
|
|
|
1506
1476
|
*/
|
|
1507
1477
|
number: {
|
|
1508
1478
|
convertToPredicate: function convertToPredicate(correctAnswer, options) {
|
|
1509
|
-
|
|
1510
|
-
var correctFloat = parseFloat($.trim(correctAnswer));
|
|
1479
|
+
var correctFloat = parseFloat(correctAnswer);
|
|
1511
1480
|
return [function (guess, maxError) {
|
|
1512
1481
|
return Math.abs(guess - correctFloat) < maxError;
|
|
1513
1482
|
}, $.extend({}, options, {
|
|
@@ -1701,16 +1670,10 @@ function inputPathsEqual(a, b) {
|
|
|
1701
1670
|
return a.length === b.length && a.every((item, index) => {
|
|
1702
1671
|
return b[index] === item;
|
|
1703
1672
|
});
|
|
1704
|
-
}
|
|
1705
|
-
// in content_internal/perseus_traversal.py
|
|
1706
|
-
// sync-start:widget-id-regex 1271666319 services/content-editing/perseus/translation/content_traversal.go
|
|
1707
|
-
// sync-start:widget-id-regex 680240484 services/static/javascript/perseus-merged-editor-package/editor.jsx
|
|
1708
|
-
// sync-start:widget-id-regex 1400368396 content_internal/perseus_traversal.py
|
|
1709
|
-
|
|
1673
|
+
}
|
|
1710
1674
|
|
|
1711
1675
|
var rWidgetRule = /^\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/;
|
|
1712
|
-
var rTypeFromWidgetId = /^([a-z-]+) ([0-9]+)$/;
|
|
1713
|
-
|
|
1676
|
+
var rTypeFromWidgetId = /^([a-z-]+) ([0-9]+)$/;
|
|
1714
1677
|
var rWidgetParts = new RegExp(rWidgetRule.source + "$");
|
|
1715
1678
|
var snowman = "\u2603";
|
|
1716
1679
|
var noScore = {
|
|
@@ -3143,11 +3106,10 @@ var warningColor = "#f86700";
|
|
|
3143
3106
|
var warningColorHover = "#df5c00";
|
|
3144
3107
|
var warningColorActive = "#c75300";
|
|
3145
3108
|
var publishBlockingErrorColor = "#be2612";
|
|
3146
|
-
var radioBorderColor$
|
|
3147
|
-
var checkedColor$1 = "#71B307";
|
|
3148
|
-
|
|
3109
|
+
var radioBorderColor$1 = "#BABEC2";
|
|
3110
|
+
var checkedColor$1 = "#71B307";
|
|
3149
3111
|
var articleMaxWidthInPx = 688;
|
|
3150
|
-
var articleMaxWidthTableInPx = 512;
|
|
3112
|
+
var articleMaxWidthTableInPx = 512;
|
|
3151
3113
|
|
|
3152
3114
|
var constants$1 = /*#__PURE__*/Object.freeze({
|
|
3153
3115
|
__proto__: null,
|
|
@@ -3212,7 +3174,7 @@ var constants$1 = /*#__PURE__*/Object.freeze({
|
|
|
3212
3174
|
warningColorHover: warningColorHover,
|
|
3213
3175
|
warningColorActive: warningColorActive,
|
|
3214
3176
|
publishBlockingErrorColor: publishBlockingErrorColor,
|
|
3215
|
-
radioBorderColor: radioBorderColor$
|
|
3177
|
+
radioBorderColor: radioBorderColor$1,
|
|
3216
3178
|
checkedColor: checkedColor$1,
|
|
3217
3179
|
articleMaxWidthInPx: articleMaxWidthInPx,
|
|
3218
3180
|
articleMaxWidthTableInPx: articleMaxWidthTableInPx
|
|
@@ -17996,17 +17958,17 @@ class Lint extends React.Component {
|
|
|
17996
17958
|
var warningTextStyle;
|
|
17997
17959
|
|
|
17998
17960
|
if (this.props.severity === 1) {
|
|
17999
|
-
severityStyle = styles$
|
|
17961
|
+
severityStyle = styles$A.indicatorError;
|
|
18000
17962
|
warningText = "Error";
|
|
18001
|
-
warningTextStyle = styles$
|
|
17963
|
+
warningTextStyle = styles$A.publishBlockingError;
|
|
18002
17964
|
} else if (this.props.severity === 2) {
|
|
18003
|
-
severityStyle = styles$
|
|
17965
|
+
severityStyle = styles$A.indicatorWarning;
|
|
18004
17966
|
warningText = "Warning";
|
|
18005
|
-
warningTextStyle = styles$
|
|
17967
|
+
warningTextStyle = styles$A.warning;
|
|
18006
17968
|
} else {
|
|
18007
|
-
severityStyle = styles$
|
|
17969
|
+
severityStyle = styles$A.indicatorGuideline;
|
|
18008
17970
|
warningText = "Recommendation";
|
|
18009
|
-
warningTextStyle = styles$
|
|
17971
|
+
warningTextStyle = styles$A.warning;
|
|
18010
17972
|
}
|
|
18011
17973
|
|
|
18012
17974
|
return /*#__PURE__*/React.createElement("a", {
|
|
@@ -18014,16 +17976,16 @@ class Lint extends React.Component {
|
|
|
18014
17976
|
target: "lint-help-window",
|
|
18015
17977
|
className: css(style)
|
|
18016
17978
|
}, /*#__PURE__*/React.createElement("span", {
|
|
18017
|
-
className: css(styles$
|
|
17979
|
+
className: css(styles$A.indicator, severityStyle)
|
|
18018
17980
|
}, this.props.severity === 1 && /*#__PURE__*/React.createElement(InlineIcon, exclamationIcon)), /*#__PURE__*/React.createElement("div", {
|
|
18019
|
-
className: css(styles$
|
|
17981
|
+
className: css(styles$A.tooltip, tooltipAbove && styles$A.tooltipAbove)
|
|
18020
17982
|
}, this.props.message.split("\n\n").map((m, i) => /*#__PURE__*/React.createElement("p", {
|
|
18021
17983
|
key: i,
|
|
18022
|
-
className: css(styles$
|
|
17984
|
+
className: css(styles$A.tooltipParagraph)
|
|
18023
17985
|
}, /*#__PURE__*/React.createElement("span", {
|
|
18024
17986
|
className: css(warningTextStyle)
|
|
18025
17987
|
}, warningText, ":", " "), m)), /*#__PURE__*/React.createElement("div", {
|
|
18026
|
-
className: css(styles$
|
|
17988
|
+
className: css(styles$A.tail, tooltipAbove && styles$A.tailAbove)
|
|
18027
17989
|
})));
|
|
18028
17990
|
});
|
|
18029
17991
|
}
|
|
@@ -18071,19 +18033,19 @@ class Lint extends React.Component {
|
|
|
18071
18033
|
|
|
18072
18034
|
if (blockHighlight) {
|
|
18073
18035
|
return /*#__PURE__*/React.createElement("span", {
|
|
18074
|
-
className: css(styles$
|
|
18075
|
-
}, this.renderLink(styles$
|
|
18036
|
+
className: css(styles$A.lintContainer, styles$A.lintContainerBlock)
|
|
18037
|
+
}, this.renderLink(styles$A.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
|
|
18076
18038
|
}
|
|
18077
18039
|
|
|
18078
18040
|
if (inline) {
|
|
18079
18041
|
return /*#__PURE__*/React.createElement("span", {
|
|
18080
|
-
className: css(styles$
|
|
18081
|
-
}, this.renderLink(styles$
|
|
18042
|
+
className: css(styles$A.lintContainer)
|
|
18043
|
+
}, this.renderLink(styles$A.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
|
|
18082
18044
|
}
|
|
18083
18045
|
|
|
18084
18046
|
return /*#__PURE__*/React.createElement("div", {
|
|
18085
|
-
className: css(styles$
|
|
18086
|
-
}, this.renderLink(styles$
|
|
18047
|
+
className: css(styles$A.lintContainer)
|
|
18048
|
+
}, this.renderLink(styles$A.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
|
|
18087
18049
|
}
|
|
18088
18050
|
|
|
18089
18051
|
} // $FlowFixMe[prop-missing]
|
|
@@ -18108,7 +18070,7 @@ _defineProperty(Lint, "propTypes", {
|
|
|
18108
18070
|
severity: PropTypes.number
|
|
18109
18071
|
});
|
|
18110
18072
|
|
|
18111
|
-
var styles$
|
|
18073
|
+
var styles$A = StyleSheet.create({
|
|
18112
18074
|
// This is the class of the outermost element.
|
|
18113
18075
|
// We use relative positioning so that the lint indicator can be
|
|
18114
18076
|
// positioned absolutely relative to the position of the linty container.
|
|
@@ -19998,7 +19960,7 @@ class Renderer extends React.Component {
|
|
|
19998
19960
|
paddingRight: margin
|
|
19999
19961
|
};
|
|
20000
19962
|
|
|
20001
|
-
var mobileInnerStyle = _objectSpread2(_objectSpread2({}, innerStyle), styles$
|
|
19963
|
+
var mobileInnerStyle = _objectSpread2(_objectSpread2({}, innerStyle), styles$z.mobileZoomableParentFix);
|
|
20002
19964
|
|
|
20003
19965
|
return /*#__PURE__*/React.createElement("div", {
|
|
20004
19966
|
key: state.key,
|
|
@@ -20136,7 +20098,7 @@ class Renderer extends React.Component {
|
|
|
20136
20098
|
paddingRight: 0
|
|
20137
20099
|
};
|
|
20138
20100
|
|
|
20139
|
-
var _mobileInnerStyle = _objectSpread2(_objectSpread2({}, _innerStyle), styles$
|
|
20101
|
+
var _mobileInnerStyle = _objectSpread2(_objectSpread2({}, _innerStyle), styles$z.mobileZoomableParentFix);
|
|
20140
20102
|
|
|
20141
20103
|
var wrappedOutput = /*#__PURE__*/React.createElement("div", {
|
|
20142
20104
|
style: _objectSpread2(_objectSpread2({}, _mobileInnerStyle), {}, {
|
|
@@ -20815,7 +20777,7 @@ _defineProperty(Renderer, "defaultProps", {
|
|
|
20815
20777
|
linterContext: PerseusLinter.linterContextDefault
|
|
20816
20778
|
});
|
|
20817
20779
|
|
|
20818
|
-
var styles$
|
|
20780
|
+
var styles$z = {
|
|
20819
20781
|
mobileZoomableParentFix: {
|
|
20820
20782
|
// NOTE(abdul): There is an issue where transform animations will
|
|
20821
20783
|
// cause the Zoomable component to disappear when running on the
|
|
@@ -22991,36 +22953,37 @@ _defineProperty(PossibleAnswers, "propTypes", {
|
|
|
22991
22953
|
answers: PropTypes.arrayOf(PropTypes.string)
|
|
22992
22954
|
});
|
|
22993
22955
|
|
|
22994
|
-
var _excluded$
|
|
22995
|
-
|
|
22996
|
-
|
|
22997
|
-
|
|
22998
|
-
|
|
22999
|
-
|
|
23000
|
-
|
|
23001
|
-
|
|
23002
|
-
componentDidMount
|
|
22956
|
+
var _excluded$3 = ["keypadElement", "onFocus", "value"];
|
|
22957
|
+
class SimpleKeypadInput extends React.Component {
|
|
22958
|
+
constructor() {
|
|
22959
|
+
super(...arguments);
|
|
22960
|
+
|
|
22961
|
+
_defineProperty(this, "_isMounted", false);
|
|
22962
|
+
}
|
|
22963
|
+
|
|
22964
|
+
componentDidMount() {
|
|
23003
22965
|
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
23004
22966
|
// this.isMounted() but is still considered an anti-pattern.
|
|
23005
22967
|
this._isMounted = true;
|
|
23006
|
-
}
|
|
23007
|
-
|
|
22968
|
+
}
|
|
22969
|
+
|
|
22970
|
+
componentWillUnmount() {
|
|
23008
22971
|
this._isMounted = false;
|
|
23009
|
-
}
|
|
22972
|
+
}
|
|
23010
22973
|
|
|
23011
22974
|
focus() {
|
|
23012
22975
|
// $FlowFixMe[object-this-reference]
|
|
23013
22976
|
this.refs.input.focus(); // eslint-disable-line react/no-string-refs
|
|
23014
|
-
}
|
|
22977
|
+
}
|
|
23015
22978
|
|
|
23016
22979
|
blur() {
|
|
23017
22980
|
// $FlowFixMe[object-this-reference]
|
|
23018
22981
|
this.refs.input.blur(); // eslint-disable-line react/no-string-refs
|
|
23019
|
-
}
|
|
22982
|
+
}
|
|
23020
22983
|
|
|
23021
|
-
getValue
|
|
22984
|
+
getValue() {
|
|
23022
22985
|
return this.props.value;
|
|
23023
|
-
}
|
|
22986
|
+
}
|
|
23024
22987
|
|
|
23025
22988
|
render() {
|
|
23026
22989
|
// $FlowFixMe[object-this-reference]
|
|
@@ -23036,7 +22999,7 @@ var SimpleKeypadInput = createReactClass({
|
|
|
23036
22999
|
onFocus: _onFocus,
|
|
23037
23000
|
value
|
|
23038
23001
|
} = _this$props,
|
|
23039
|
-
rest = _objectWithoutProperties(_this$props, _excluded$
|
|
23002
|
+
rest = _objectWithoutProperties(_this$props, _excluded$3);
|
|
23040
23003
|
|
|
23041
23004
|
return /*#__PURE__*/React.createElement(KeypadInput // eslint-disable-next-line react/no-string-refs
|
|
23042
23005
|
, _extends({
|
|
@@ -23059,7 +23022,12 @@ var SimpleKeypadInput = createReactClass({
|
|
|
23059
23022
|
}, rest));
|
|
23060
23023
|
}
|
|
23061
23024
|
|
|
23062
|
-
}
|
|
23025
|
+
}
|
|
23026
|
+
SimpleKeypadInput.propTypes = {
|
|
23027
|
+
keypadElement: keypadElementPropType,
|
|
23028
|
+
onFocus: PropTypes.func,
|
|
23029
|
+
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
|
23030
|
+
};
|
|
23063
23031
|
|
|
23064
23032
|
var ParseTex$1 = TexWrangler.parseTex;
|
|
23065
23033
|
var answerTypes = {
|
|
@@ -23314,9 +23282,13 @@ class InputNumber extends React.Component {
|
|
|
23314
23282
|
|
|
23315
23283
|
if (rubric.answerType == null) {
|
|
23316
23284
|
rubric.answerType = "number";
|
|
23317
|
-
}
|
|
23285
|
+
} // note(matthewc): this will get immediately parsed again by
|
|
23286
|
+
// `KhanAnswerTypes.number.convertToPredicate`, but a string is
|
|
23287
|
+
// expected here
|
|
23288
|
+
|
|
23318
23289
|
|
|
23319
|
-
var
|
|
23290
|
+
var stringValue = "".concat(rubric.value);
|
|
23291
|
+
var val = KhanAnswerTypes.number.createValidatorFunctional(stringValue, {
|
|
23320
23292
|
simplify: rubric.simplify,
|
|
23321
23293
|
inexact: rubric.inexact || undefined,
|
|
23322
23294
|
maxError: rubric.maxError,
|
|
@@ -23442,6 +23414,8 @@ class NumericInput extends React.Component {
|
|
|
23442
23414
|
constructor() {
|
|
23443
23415
|
super(...arguments);
|
|
23444
23416
|
|
|
23417
|
+
_defineProperty(this, "inputRef", void 0);
|
|
23418
|
+
|
|
23445
23419
|
_defineProperty(this, "state", {
|
|
23446
23420
|
// keeps track of the other set of values when switching
|
|
23447
23421
|
// between 0 and finite solutions
|
|
@@ -23460,9 +23434,8 @@ class NumericInput extends React.Component {
|
|
|
23460
23434
|
correct = score.type === "points" && score.earned === score.total;
|
|
23461
23435
|
|
|
23462
23436
|
if (!correct) {
|
|
23463
|
-
var correctAnswers =
|
|
23464
|
-
|
|
23465
|
-
var answerStrings = _.map(correctAnswers, answer => {
|
|
23437
|
+
var correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
23438
|
+
var answerStrings = correctAnswers.map(answer => {
|
|
23466
23439
|
// Figure out how this answer is supposed to be
|
|
23467
23440
|
// displayed
|
|
23468
23441
|
var format = "decimal";
|
|
@@ -23483,7 +23456,6 @@ class NumericInput extends React.Component {
|
|
|
23483
23456
|
|
|
23484
23457
|
return answerString;
|
|
23485
23458
|
});
|
|
23486
|
-
|
|
23487
23459
|
answerBlurb = /*#__PURE__*/React.createElement(PossibleAnswers, {
|
|
23488
23460
|
answers: answerStrings
|
|
23489
23461
|
});
|
|
@@ -23497,7 +23469,7 @@ class NumericInput extends React.Component {
|
|
|
23497
23469
|
var _this$props$answerFor;
|
|
23498
23470
|
|
|
23499
23471
|
// if the set of specified forms are empty, allow all forms
|
|
23500
|
-
var forms = ((_this$props$answerFor = this.props.answerForms) === null || _this$props$answerFor === void 0 ? void 0 : _this$props$answerFor.length) !== 0 ? this.props.answerForms :
|
|
23472
|
+
var forms = ((_this$props$answerFor = this.props.answerForms) === null || _this$props$answerFor === void 0 ? void 0 : _this$props$answerFor.length) !== 0 ? this.props.answerForms : Object.keys(formExamples).map(name => {
|
|
23501
23473
|
return {
|
|
23502
23474
|
name: name,
|
|
23503
23475
|
simplify: "required"
|
|
@@ -23523,8 +23495,7 @@ class NumericInput extends React.Component {
|
|
|
23523
23495
|
|
|
23524
23496
|
var answerFormNames = _.uniq((_this$props$answerFor3 = this.props.answerForms) === null || _this$props$answerFor3 === void 0 ? void 0 : _this$props$answerFor3.map(form => form.name));
|
|
23525
23497
|
|
|
23526
|
-
var allFormsAccepted = answerFormNames.length >=
|
|
23527
|
-
|
|
23498
|
+
var allFormsAccepted = answerFormNames.length >= Object.keys(formExamples).length;
|
|
23528
23499
|
return !noFormsAccepted && !allFormsAccepted;
|
|
23529
23500
|
});
|
|
23530
23501
|
|
|
@@ -23533,20 +23504,22 @@ class NumericInput extends React.Component {
|
|
|
23533
23504
|
});
|
|
23534
23505
|
|
|
23535
23506
|
_defineProperty(this, "focus", () => {
|
|
23536
|
-
|
|
23537
|
-
|
|
23507
|
+
var _this$inputRef;
|
|
23508
|
+
|
|
23509
|
+
(_this$inputRef = this.inputRef) === null || _this$inputRef === void 0 ? void 0 : _this$inputRef.focus();
|
|
23538
23510
|
return true;
|
|
23539
23511
|
});
|
|
23540
23512
|
|
|
23541
|
-
_defineProperty(this, "focusInputPath",
|
|
23542
|
-
|
|
23543
|
-
|
|
23544
|
-
this.
|
|
23513
|
+
_defineProperty(this, "focusInputPath", () => {
|
|
23514
|
+
var _this$inputRef2;
|
|
23515
|
+
|
|
23516
|
+
(_this$inputRef2 = this.inputRef) === null || _this$inputRef2 === void 0 ? void 0 : _this$inputRef2.focus();
|
|
23545
23517
|
});
|
|
23546
23518
|
|
|
23547
|
-
_defineProperty(this, "blurInputPath",
|
|
23548
|
-
|
|
23549
|
-
|
|
23519
|
+
_defineProperty(this, "blurInputPath", () => {
|
|
23520
|
+
var _this$inputRef3;
|
|
23521
|
+
|
|
23522
|
+
(_this$inputRef3 = this.inputRef) === null || _this$inputRef3 === void 0 ? void 0 : _this$inputRef3.blur();
|
|
23550
23523
|
});
|
|
23551
23524
|
|
|
23552
23525
|
_defineProperty(this, "getInputPaths", () => {
|
|
@@ -23614,9 +23587,8 @@ class NumericInput extends React.Component {
|
|
|
23614
23587
|
}
|
|
23615
23588
|
|
|
23616
23589
|
static getOneCorrectAnswerFromRubric(rubric) {
|
|
23617
|
-
var correctAnswers =
|
|
23618
|
-
|
|
23619
|
-
var answerStrings = _.map(correctAnswers, answer => {
|
|
23590
|
+
var correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
23591
|
+
var answerStrings = correctAnswers.map(answer => {
|
|
23620
23592
|
// Figure out how this answer is supposed to be
|
|
23621
23593
|
// displayed
|
|
23622
23594
|
var format = "decimal";
|
|
@@ -23646,31 +23618,30 @@ class NumericInput extends React.Component {
|
|
|
23646
23618
|
}
|
|
23647
23619
|
|
|
23648
23620
|
static validate(useInput, rubric) {
|
|
23649
|
-
var allAnswerForms =
|
|
23650
|
-
|
|
23651
|
-
var createValidator = answer =>
|
|
23652
|
-
|
|
23653
|
-
|
|
23654
|
-
|
|
23655
|
-
|
|
23656
|
-
|
|
23657
|
-
|
|
23658
|
-
|
|
23621
|
+
var allAnswerForms = answerFormButtons.map(e => e["value"]);
|
|
23622
|
+
|
|
23623
|
+
var createValidator = answer => {
|
|
23624
|
+
var stringAnswer = "".concat(answer.value);
|
|
23625
|
+
return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
|
|
23626
|
+
message: answer.message,
|
|
23627
|
+
simplify: answer.status === "correct" ? answer.simplify : "optional",
|
|
23628
|
+
inexact: true,
|
|
23629
|
+
// TODO(merlob) backfill / delete
|
|
23630
|
+
maxError: answer.maxError,
|
|
23631
|
+
forms: answer.strict && answer.answerForms && answer.answerForms.length !== 0 ? answer.answerForms : allAnswerForms
|
|
23632
|
+
});
|
|
23633
|
+
}; // We may have received TeX; try to parse it before grading.
|
|
23659
23634
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
23660
23635
|
|
|
23661
23636
|
|
|
23662
23637
|
var currentValue = ParseTex(useInput.currentValue);
|
|
23663
|
-
|
|
23664
|
-
var correctAnswers = _.where(rubric.answers, {
|
|
23665
|
-
status: "correct"
|
|
23666
|
-
});
|
|
23667
|
-
|
|
23638
|
+
var correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
23668
23639
|
var normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1); // Look through all correct answers for one that matches either
|
|
23669
23640
|
// precisely or approximately and return the appropriate message:
|
|
23670
23641
|
// - if precise, return the message that the answer came with
|
|
23671
23642
|
// - if it needs to be simplified, etc., show that message
|
|
23672
23643
|
|
|
23673
|
-
var result =
|
|
23644
|
+
var result = correctAnswers.map(answer => {
|
|
23674
23645
|
// The coefficient is an attribute of the widget
|
|
23675
23646
|
var localValue = currentValue;
|
|
23676
23647
|
|
|
@@ -23684,22 +23655,17 @@ class NumericInput extends React.Component {
|
|
|
23684
23655
|
|
|
23685
23656
|
var validate = createValidator(answer);
|
|
23686
23657
|
return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
|
|
23687
|
-
})
|
|
23658
|
+
}).find(match => match.correct || match.empty);
|
|
23688
23659
|
|
|
23689
23660
|
if (!result) {
|
|
23690
23661
|
// Otherwise, if the guess is not correct
|
|
23691
|
-
var otherAnswers = [].concat(
|
|
23692
|
-
status: "ungraded"
|
|
23693
|
-
}), _.where(rubric.answers, {
|
|
23694
|
-
status: "wrong"
|
|
23695
|
-
})); // Look through all other answers and if one matches either
|
|
23662
|
+
var otherAnswers = [].concat(rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong")); // Look through all other answers and if one matches either
|
|
23696
23663
|
// precisely or approximately return the answer's message
|
|
23697
23664
|
|
|
23698
|
-
var match =
|
|
23665
|
+
var match = otherAnswers.find(answer => {
|
|
23699
23666
|
var validate = createValidator(answer);
|
|
23700
23667
|
return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
|
|
23701
23668
|
});
|
|
23702
|
-
|
|
23703
23669
|
result = {
|
|
23704
23670
|
empty: match ? match.status === "ungraded" : false,
|
|
23705
23671
|
correct: match ? match.status === "correct" : false,
|
|
@@ -23746,9 +23712,8 @@ class NumericInput extends React.Component {
|
|
|
23746
23712
|
|
|
23747
23713
|
if (this.props.apiOptions.customKeypad) {
|
|
23748
23714
|
// TODO(charlie): Support "Review Mode".
|
|
23749
|
-
return maybeRightAlignKeypadInput( /*#__PURE__*/React.createElement(SimpleKeypadInput
|
|
23750
|
-
|
|
23751
|
-
ref: "input",
|
|
23715
|
+
return maybeRightAlignKeypadInput( /*#__PURE__*/React.createElement(SimpleKeypadInput, {
|
|
23716
|
+
ref: _ref => this.inputRef = _ref,
|
|
23752
23717
|
value: this.props.currentValue,
|
|
23753
23718
|
keypadElement: this.props.keypadElement,
|
|
23754
23719
|
onChange: this.handleChange,
|
|
@@ -23757,9 +23722,8 @@ class NumericInput extends React.Component {
|
|
|
23757
23722
|
}));
|
|
23758
23723
|
}
|
|
23759
23724
|
|
|
23760
|
-
var input = /*#__PURE__*/React.createElement(InputWithExamples
|
|
23761
|
-
|
|
23762
|
-
ref: "input",
|
|
23725
|
+
var input = /*#__PURE__*/React.createElement(InputWithExamples, {
|
|
23726
|
+
ref: _ref2 => this.inputRef = _ref2,
|
|
23763
23727
|
value: this.props.currentValue,
|
|
23764
23728
|
onChange: this.handleChange,
|
|
23765
23729
|
className: classNames(classes),
|
|
@@ -23826,7 +23790,7 @@ var unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
|
23826
23790
|
// two elements are equal, and returns a list of unique elements. This is
|
|
23827
23791
|
// just a helper function here, but works generally.
|
|
23828
23792
|
var uniqueBy = function uniqueBy(list, iteratee) {
|
|
23829
|
-
return
|
|
23793
|
+
return list.reduce((uniqueList, element) => {
|
|
23830
23794
|
// For each element, decide whether it's already in the list of
|
|
23831
23795
|
// unique items.
|
|
23832
23796
|
var inList = _.find(uniqueList, iteratee.bind(null, element));
|
|
@@ -23840,13 +23804,13 @@ var unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
|
23840
23804
|
}; // Pull out all of the forms from the different lists.
|
|
23841
23805
|
|
|
23842
23806
|
|
|
23843
|
-
var allForms =
|
|
23844
|
-
|
|
23807
|
+
var allForms = answerFormsList.flat(); // Pull out the unique forms using uniqueBy.
|
|
23845
23808
|
|
|
23846
23809
|
var uniqueForms = uniqueBy(allForms, _.isEqual); // Sort them by the order they appear in the `formExamples` list.
|
|
23847
23810
|
|
|
23811
|
+
var formExampleKeys = Object.keys(formExamples);
|
|
23848
23812
|
return _.sortBy(uniqueForms, form => {
|
|
23849
|
-
return
|
|
23813
|
+
return formExampleKeys.indexOf(form.name);
|
|
23850
23814
|
});
|
|
23851
23815
|
};
|
|
23852
23816
|
// This function checks if the user inputted a percent value, parsing
|
|
@@ -23855,7 +23819,7 @@ var unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
|
23855
23819
|
// can accept several input forms with or without "%", the decision
|
|
23856
23820
|
// to parse based on the presence of "%" in the input, is so that we
|
|
23857
23821
|
// don't accidently scale the user typed value before grading, CP-930.
|
|
23858
|
-
|
|
23822
|
+
function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
|
|
23859
23823
|
// If the input value is not a string ending with "%", then there's
|
|
23860
23824
|
// nothing more to do. The value will be graded as inputted by user.
|
|
23861
23825
|
if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
|
|
@@ -23880,13 +23844,13 @@ var maybeParsePercentInput = (inputValue, normalizedAnswerExpected) => {
|
|
|
23880
23844
|
|
|
23881
23845
|
|
|
23882
23846
|
return value;
|
|
23883
|
-
}
|
|
23847
|
+
}
|
|
23884
23848
|
|
|
23885
23849
|
var propsTransform = function propsTransform(widgetOptions) {
|
|
23886
23850
|
var rendererProps = _.extend(_.omit(widgetOptions, "answers"), {
|
|
23887
23851
|
answerForms: unionAnswerForms( // Pull out the name of each form and whether that form has
|
|
23888
23852
|
// required simplification.
|
|
23889
|
-
|
|
23853
|
+
widgetOptions.answers.map(answer => {
|
|
23890
23854
|
return _.map(answer.answerForms, form => {
|
|
23891
23855
|
return {
|
|
23892
23856
|
simplify: answer.simplify,
|
|
@@ -24122,7 +24086,7 @@ var mediaQueries = {
|
|
|
24122
24086
|
var {
|
|
24123
24087
|
zIndexAboveScratchpad,
|
|
24124
24088
|
zIndexInteractiveComponent,
|
|
24125
|
-
radioBorderColor
|
|
24089
|
+
radioBorderColor,
|
|
24126
24090
|
checkedColor,
|
|
24127
24091
|
circleSize,
|
|
24128
24092
|
radioMarginWidth
|
|
@@ -24177,7 +24141,7 @@ var sharedStyles = StyleSheet.create({
|
|
|
24177
24141
|
},
|
|
24178
24142
|
backgroundColor: "#fff",
|
|
24179
24143
|
border: "2px solid #fff",
|
|
24180
|
-
boxShadow: "0 0px 0px 1px ".concat(radioBorderColor
|
|
24144
|
+
boxShadow: "0 0px 0px 1px ".concat(radioBorderColor),
|
|
24181
24145
|
outline: "none",
|
|
24182
24146
|
boxSizing: "border-box",
|
|
24183
24147
|
flexShrink: 0,
|
|
@@ -24430,35 +24394,65 @@ _defineProperty(Icon, "defaultProps", {
|
|
|
24430
24394
|
color: "currentColor"
|
|
24431
24395
|
});
|
|
24432
24396
|
|
|
24433
|
-
|
|
24434
|
-
|
|
24435
|
-
|
|
24436
|
-
|
|
24437
|
-
|
|
24438
|
-
|
|
24439
|
-
|
|
24440
|
-
|
|
24441
|
-
|
|
24442
|
-
|
|
24397
|
+
function getA11yText(letter, checked, correct, crossedOut, showCorrectness) {
|
|
24398
|
+
// There are two pieces of metadata we want to add to each a11yText:
|
|
24399
|
+
// whether the answer was checked/crossed-out/neither, and whether the
|
|
24400
|
+
// answer is correct/incorrect/not-yet-revealed.
|
|
24401
|
+
//
|
|
24402
|
+
// Translation is tricky for cross-product situations like this, so
|
|
24403
|
+
// we've just enumerated all 9 possibilities as separate strings.
|
|
24404
|
+
if (showCorrectness && correct) {
|
|
24405
|
+
if (checked) {
|
|
24406
|
+
return i18n._("(Choice %(letter)s, Checked, Correct)", {
|
|
24407
|
+
letter
|
|
24408
|
+
});
|
|
24409
|
+
}
|
|
24410
|
+
|
|
24411
|
+
if (crossedOut) {
|
|
24412
|
+
return i18n._("(Choice %(letter)s, Crossed out, Correct)", {
|
|
24413
|
+
letter
|
|
24414
|
+
});
|
|
24415
|
+
}
|
|
24416
|
+
|
|
24417
|
+
return i18n._("(Choice %(letter)s, Correct)", {
|
|
24418
|
+
letter
|
|
24419
|
+
});
|
|
24443
24420
|
}
|
|
24444
24421
|
|
|
24445
|
-
|
|
24422
|
+
if (showCorrectness && !correct) {
|
|
24423
|
+
if (checked) {
|
|
24424
|
+
return i18n._("(Choice %(letter)s, Checked, Incorrect)", {
|
|
24425
|
+
letter
|
|
24426
|
+
});
|
|
24427
|
+
}
|
|
24446
24428
|
|
|
24447
|
-
|
|
24448
|
-
|
|
24449
|
-
|
|
24450
|
-
});
|
|
24429
|
+
if (crossedOut) {
|
|
24430
|
+
return i18n._("(Choice %(letter)s, Crossed out, Incorrect)", {
|
|
24431
|
+
letter
|
|
24432
|
+
});
|
|
24433
|
+
}
|
|
24451
24434
|
|
|
24452
|
-
|
|
24453
|
-
|
|
24454
|
-
|
|
24455
|
-
display: "inline-block",
|
|
24456
|
-
borderRadius: "50%",
|
|
24457
|
-
borderWidth: 2,
|
|
24458
|
-
padding: 2,
|
|
24459
|
-
borderStyle: "solid"
|
|
24435
|
+
return i18n._("(Choice %(letter)s, Incorrect)", {
|
|
24436
|
+
letter
|
|
24437
|
+
});
|
|
24460
24438
|
}
|
|
24461
|
-
|
|
24439
|
+
|
|
24440
|
+
if (checked) {
|
|
24441
|
+
return i18n._("(Choice %(letter)s, Checked)", {
|
|
24442
|
+
letter
|
|
24443
|
+
});
|
|
24444
|
+
}
|
|
24445
|
+
|
|
24446
|
+
if (crossedOut) {
|
|
24447
|
+
return i18n._("(Choice %(letter)s, Crossed out)", {
|
|
24448
|
+
letter
|
|
24449
|
+
});
|
|
24450
|
+
}
|
|
24451
|
+
|
|
24452
|
+
return i18n._("(Choice %(letter)s)", {
|
|
24453
|
+
letter
|
|
24454
|
+
});
|
|
24455
|
+
}
|
|
24462
24456
|
|
|
24463
24457
|
/**
|
|
24464
24458
|
* Given a choice's position in the radio widget, return the corresponding
|
|
@@ -24490,179 +24484,57 @@ function getChoiceLetter(pos) {
|
|
|
24490
24484
|
return " ";
|
|
24491
24485
|
}
|
|
24492
24486
|
|
|
24493
|
-
|
|
24494
|
-
|
|
24495
|
-
|
|
24496
|
-
// TODO(amy): figure out a better scheme for specifying these
|
|
24497
|
-
// styles that isn't such a pain to grok. See some neat ideas
|
|
24498
|
-
// from MDR in https://phabricator.khanacademy.org/D35249.
|
|
24499
|
-
constructStyles(reviewMode, correct, checked) {
|
|
24500
|
-
var backgroundColor;
|
|
24501
|
-
var borderColor = satBlue;
|
|
24502
|
-
var color = satBlue;
|
|
24503
|
-
|
|
24504
|
-
if (reviewMode) {
|
|
24505
|
-
if (correct) {
|
|
24506
|
-
borderColor = satCorrectColor;
|
|
24507
|
-
color = checked ? white$1 : satCorrectColor;
|
|
24508
|
-
backgroundColor = checked ? satCorrectColor : white$1;
|
|
24509
|
-
} else if (checked) {
|
|
24510
|
-
borderColor = satIncorrectColor;
|
|
24511
|
-
color = white$1;
|
|
24512
|
-
backgroundColor = satIncorrectColor;
|
|
24513
|
-
}
|
|
24514
|
-
} else if (checked) {
|
|
24515
|
-
color = white$1;
|
|
24516
|
-
backgroundColor = satBlue;
|
|
24517
|
-
}
|
|
24518
|
-
|
|
24519
|
-
return {
|
|
24520
|
-
color,
|
|
24521
|
-
backgroundColor,
|
|
24522
|
-
borderColor
|
|
24523
|
-
};
|
|
24524
|
-
}
|
|
24525
|
-
|
|
24526
|
-
render() {
|
|
24527
|
-
var {
|
|
24528
|
-
letter,
|
|
24529
|
-
a11yText,
|
|
24530
|
-
reviewMode,
|
|
24531
|
-
checked,
|
|
24532
|
-
correct,
|
|
24533
|
-
crossedOut
|
|
24534
|
-
} = this.props;
|
|
24535
|
-
var {
|
|
24536
|
-
color,
|
|
24537
|
-
backgroundColor,
|
|
24538
|
-
borderColor
|
|
24539
|
-
} = this.constructStyles(reviewMode, correct, checked);
|
|
24540
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
24541
|
-
className: css(styles$t.iconWrapper)
|
|
24542
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
24543
|
-
className: css(styles$t.satCircle),
|
|
24544
|
-
style: {
|
|
24545
|
-
backgroundColor,
|
|
24546
|
-
borderColor
|
|
24547
|
-
}
|
|
24548
|
-
}), /*#__PURE__*/React.createElement("div", {
|
|
24549
|
-
style: {
|
|
24550
|
-
color
|
|
24551
|
-
},
|
|
24552
|
-
className: css(styles$t.letter)
|
|
24553
|
-
}, /*#__PURE__*/React.createElement("span", {
|
|
24554
|
-
className: "perseus-sr-only"
|
|
24555
|
-
}, a11yText), /*#__PURE__*/React.createElement("span", {
|
|
24556
|
-
"aria-hidden": "true"
|
|
24557
|
-
}, letter)), crossedOut && /*#__PURE__*/React.createElement(CrossOutLine, {
|
|
24558
|
-
color: borderColor,
|
|
24559
|
-
sat: true
|
|
24560
|
-
}));
|
|
24561
|
-
}
|
|
24487
|
+
/**
|
|
24488
|
+
* Renders a circular selection ring around the child.
|
|
24489
|
+
*/
|
|
24562
24490
|
|
|
24491
|
+
function FocusRing(props) {
|
|
24492
|
+
var {
|
|
24493
|
+
visible,
|
|
24494
|
+
color,
|
|
24495
|
+
children,
|
|
24496
|
+
multipleSelect
|
|
24497
|
+
} = props;
|
|
24498
|
+
var borderColor = visible ? color : "transparent";
|
|
24499
|
+
var borderRadius = multipleSelect ? 5 : "50%";
|
|
24500
|
+
var style = {
|
|
24501
|
+
borderColor,
|
|
24502
|
+
borderRadius
|
|
24503
|
+
};
|
|
24504
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
24505
|
+
"data-test-id": "focus-ring",
|
|
24506
|
+
className: css(styles$y.ring),
|
|
24507
|
+
style: style
|
|
24508
|
+
}, children);
|
|
24563
24509
|
}
|
|
24564
24510
|
|
|
24565
|
-
|
|
24566
|
-
|
|
24567
|
-
|
|
24568
|
-
|
|
24569
|
-
|
|
24570
|
-
|
|
24571
|
-
|
|
24572
|
-
|
|
24573
|
-
|
|
24574
|
-
|
|
24575
|
-
|
|
24576
|
-
|
|
24577
|
-
}
|
|
24578
|
-
|
|
24579
|
-
if (correct) {
|
|
24580
|
-
return /*#__PURE__*/React.createElement(InlineIcon, _extends({}, iconCheck, {
|
|
24581
|
-
style: {
|
|
24582
|
-
position: "relative",
|
|
24583
|
-
top: -1
|
|
24584
|
-
}
|
|
24585
|
-
}));
|
|
24586
|
-
}
|
|
24587
|
-
|
|
24588
|
-
return /*#__PURE__*/React.createElement(InlineIcon, iconMinus);
|
|
24589
|
-
} // Handle dynamic styling of the multiple choice icon. Most
|
|
24590
|
-
// MC icon styles are constant, but we do allow the caller
|
|
24591
|
-
// to specify the selected color, and thus must control styles
|
|
24592
|
-
// related to the selected state dynamically.
|
|
24593
|
-
|
|
24594
|
-
|
|
24595
|
-
getDynamicStyles() {
|
|
24596
|
-
var {
|
|
24597
|
-
checked,
|
|
24598
|
-
showCorrectness,
|
|
24599
|
-
pressed,
|
|
24600
|
-
primaryProductColor,
|
|
24601
|
-
correct,
|
|
24602
|
-
transparentBackground
|
|
24603
|
-
} = this.props;
|
|
24604
|
-
|
|
24605
|
-
if (!showCorrectness && pressed) {
|
|
24606
|
-
return {
|
|
24607
|
-
borderColor: primaryProductColor,
|
|
24608
|
-
color: primaryProductColor,
|
|
24609
|
-
backgroundColor: transparentBackground ? "transparent" : white$1
|
|
24610
|
-
};
|
|
24611
|
-
}
|
|
24612
|
-
|
|
24613
|
-
if (checked) {
|
|
24614
|
-
// Note: kaGreen is not only the default product color,
|
|
24615
|
-
// but also the "correctness" color
|
|
24616
|
-
var bg = showCorrectness && correct ? Color.green : primaryProductColor;
|
|
24617
|
-
return {
|
|
24618
|
-
color: white$1,
|
|
24619
|
-
backgroundColor: bg,
|
|
24620
|
-
borderColor: bg
|
|
24621
|
-
};
|
|
24622
|
-
}
|
|
24623
|
-
|
|
24624
|
-
return {
|
|
24625
|
-
borderColor: Color.offBlack64,
|
|
24626
|
-
color: Color.offBlack64
|
|
24627
|
-
};
|
|
24511
|
+
FocusRing.defaultProps = {
|
|
24512
|
+
visible: true,
|
|
24513
|
+
color: kaGreen$1,
|
|
24514
|
+
multipleSelect: false
|
|
24515
|
+
};
|
|
24516
|
+
var styles$y = StyleSheet.create({
|
|
24517
|
+
ring: {
|
|
24518
|
+
margin: "auto",
|
|
24519
|
+
display: "inline-block",
|
|
24520
|
+
borderWidth: 2,
|
|
24521
|
+
padding: 2,
|
|
24522
|
+
borderStyle: "solid"
|
|
24628
24523
|
}
|
|
24524
|
+
});
|
|
24629
24525
|
|
|
24630
|
-
|
|
24631
|
-
|
|
24632
|
-
|
|
24633
|
-
|
|
24634
|
-
|
|
24635
|
-
|
|
24636
|
-
correct,
|
|
24637
|
-
focused,
|
|
24638
|
-
primaryProductColor,
|
|
24639
|
-
previouslyAnswered
|
|
24640
|
-
} = this.props;
|
|
24641
|
-
var dynamicStyles = this.getDynamicStyles();
|
|
24642
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
24643
|
-
className: css(styles$t.iconWrapper)
|
|
24644
|
-
}, /*#__PURE__*/React.createElement(FocusRing, {
|
|
24645
|
-
color: primaryProductColor,
|
|
24646
|
-
visible: focused
|
|
24647
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
24648
|
-
style: dynamicStyles,
|
|
24649
|
-
className: 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
|
|
24650
|
-
// radio icon
|
|
24651
|
-
,
|
|
24652
|
-
"data-is-radio-icon": true
|
|
24653
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
24654
|
-
className: "perseus-sr-only"
|
|
24655
|
-
}, a11yText), /*#__PURE__*/React.createElement("div", {
|
|
24656
|
-
"aria-hidden": true
|
|
24657
|
-
}, this.getChoiceInner()))), crossedOut && /*#__PURE__*/React.createElement(CrossOutLine, {
|
|
24658
|
-
color: dynamicStyles.borderColor
|
|
24659
|
-
}));
|
|
24526
|
+
var SAT_ICON_SIZE = 25;
|
|
24527
|
+
var LIBRARY_ICON_SIZE = 24;
|
|
24528
|
+
var styles$x = StyleSheet.create({
|
|
24529
|
+
iconWrapper: {
|
|
24530
|
+
display: "inline-block",
|
|
24531
|
+
position: "relative"
|
|
24660
24532
|
}
|
|
24533
|
+
});
|
|
24661
24534
|
|
|
24662
|
-
|
|
24535
|
+
/* Component for rendering a letter icon in radio choice */
|
|
24663
24536
|
// extra space for the cute rounded line cap.
|
|
24664
24537
|
|
|
24665
|
-
|
|
24666
24538
|
var CROSS_OUT_LINE_PADDING = 2;
|
|
24667
24539
|
var CROSS_OUT_LINE_SIZE = LIBRARY_ICON_SIZE + CROSS_OUT_LINE_PADDING * 2;
|
|
24668
24540
|
var SAT_CROSS_OUT_LINE_SIZE = SAT_ICON_SIZE + CROSS_OUT_LINE_PADDING * 2;
|
|
@@ -24677,7 +24549,7 @@ function CrossOutLine(props) {
|
|
|
24677
24549
|
width: crossOutLineSize,
|
|
24678
24550
|
height: crossOutLineSize,
|
|
24679
24551
|
viewBox: "0 0 ".concat(crossOutLineSize, " ").concat(crossOutLineSize),
|
|
24680
|
-
className: css(styles$
|
|
24552
|
+
className: css(styles$w.crossOutLine)
|
|
24681
24553
|
}, /*#__PURE__*/React.createElement("line", {
|
|
24682
24554
|
// The line stretches from the bottom-left to top-right.
|
|
24683
24555
|
// We don't quite go to the _very_ corner, because the cute
|
|
@@ -24692,151 +24564,124 @@ function CrossOutLine(props) {
|
|
|
24692
24564
|
}));
|
|
24693
24565
|
}
|
|
24694
24566
|
|
|
24695
|
-
|
|
24696
|
-
|
|
24697
|
-
|
|
24698
|
-
|
|
24699
|
-
|
|
24700
|
-
|
|
24701
|
-
|
|
24702
|
-
|
|
24703
|
-
// whether the answer was checked/crossed-out/neither, and whether the
|
|
24704
|
-
// answer is correct/incorrect/not-yet-revealed.
|
|
24705
|
-
//
|
|
24706
|
-
// Translation is tricky for cross-product situations like this, so
|
|
24707
|
-
// we've just enumerated all 9 possibilities as separate strings.
|
|
24708
|
-
|
|
24709
|
-
if (showCorrectness && correct) {
|
|
24710
|
-
if (checked) {
|
|
24711
|
-
return i18n._("(Choice %(letter)s, Checked, Correct)", {
|
|
24712
|
-
letter
|
|
24713
|
-
});
|
|
24714
|
-
}
|
|
24715
|
-
|
|
24716
|
-
if (crossedOut) {
|
|
24717
|
-
return i18n._("(Choice %(letter)s, Crossed out, Correct)", {
|
|
24718
|
-
letter
|
|
24719
|
-
});
|
|
24720
|
-
}
|
|
24567
|
+
var styles$w = StyleSheet.create({
|
|
24568
|
+
crossOutLine: {
|
|
24569
|
+
// Center the icon within the container.
|
|
24570
|
+
position: "absolute",
|
|
24571
|
+
top: "calc(50% - ".concat(CROSS_OUT_LINE_SIZE / 2, "px)"),
|
|
24572
|
+
left: "calc(50% - ".concat(CROSS_OUT_LINE_SIZE / 2, "px)")
|
|
24573
|
+
}
|
|
24574
|
+
});
|
|
24721
24575
|
|
|
24722
|
-
|
|
24723
|
-
|
|
24724
|
-
|
|
24725
|
-
|
|
24576
|
+
function ChoiceInner(props) {
|
|
24577
|
+
var {
|
|
24578
|
+
letter,
|
|
24579
|
+
showCorrectness,
|
|
24580
|
+
correct
|
|
24581
|
+
} = props;
|
|
24726
24582
|
|
|
24727
|
-
|
|
24728
|
-
|
|
24729
|
-
|
|
24730
|
-
letter
|
|
24731
|
-
});
|
|
24732
|
-
}
|
|
24583
|
+
if (!showCorrectness) {
|
|
24584
|
+
return letter;
|
|
24585
|
+
}
|
|
24733
24586
|
|
|
24734
|
-
|
|
24735
|
-
|
|
24736
|
-
|
|
24737
|
-
|
|
24587
|
+
if (correct) {
|
|
24588
|
+
return /*#__PURE__*/React.createElement(InlineIcon, _extends({}, iconCheck, {
|
|
24589
|
+
style: {
|
|
24590
|
+
position: "relative",
|
|
24591
|
+
top: -1
|
|
24738
24592
|
}
|
|
24739
|
-
|
|
24740
|
-
return i18n._("(Choice %(letter)s, Incorrect)", {
|
|
24741
|
-
letter
|
|
24742
|
-
});
|
|
24743
|
-
}
|
|
24744
|
-
|
|
24745
|
-
if (checked) {
|
|
24746
|
-
return i18n._("(Choice %(letter)s, Checked)", {
|
|
24747
|
-
letter
|
|
24748
|
-
});
|
|
24749
|
-
}
|
|
24750
|
-
|
|
24751
|
-
if (crossedOut) {
|
|
24752
|
-
return i18n._("(Choice %(letter)s, Crossed out)", {
|
|
24753
|
-
letter
|
|
24754
|
-
});
|
|
24755
|
-
}
|
|
24756
|
-
|
|
24757
|
-
return i18n._("(Choice %(letter)s)", {
|
|
24758
|
-
letter
|
|
24759
|
-
});
|
|
24593
|
+
}));
|
|
24760
24594
|
}
|
|
24761
24595
|
|
|
24762
|
-
|
|
24763
|
-
|
|
24764
|
-
|
|
24765
|
-
|
|
24766
|
-
|
|
24767
|
-
|
|
24768
|
-
|
|
24769
|
-
|
|
24770
|
-
|
|
24771
|
-
|
|
24772
|
-
|
|
24773
|
-
|
|
24774
|
-
|
|
24775
|
-
|
|
24776
|
-
|
|
24777
|
-
|
|
24596
|
+
return /*#__PURE__*/React.createElement(InlineIcon, iconMinus);
|
|
24597
|
+
} // Handle dynamic styling of the multiple choice icon. Most
|
|
24598
|
+
// MC icon styles are constant, but we do allow the caller
|
|
24599
|
+
// to specify the selected color, and thus must control styles
|
|
24600
|
+
// related to the selected state dynamically.
|
|
24601
|
+
|
|
24602
|
+
|
|
24603
|
+
function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, primaryProductColor, correct, transparentBackground) {
|
|
24604
|
+
var backgroundColor;
|
|
24605
|
+
var borderColor;
|
|
24606
|
+
var color;
|
|
24607
|
+
|
|
24608
|
+
if (!showCorrectness && pressed) {
|
|
24609
|
+
borderColor = primaryProductColor;
|
|
24610
|
+
color = primaryProductColor;
|
|
24611
|
+
backgroundColor = transparentBackground ? "transparent" : white$1;
|
|
24612
|
+
} else if (checked) {
|
|
24613
|
+
// Note: kaGreen is not only the default product color,
|
|
24614
|
+
// but also the "correctness" color
|
|
24615
|
+
var bg = showCorrectness && correct ? Color.green : primaryProductColor;
|
|
24616
|
+
color = white$1;
|
|
24617
|
+
backgroundColor = bg;
|
|
24618
|
+
borderColor = bg;
|
|
24619
|
+
} else {
|
|
24620
|
+
borderColor = Color.offBlack64;
|
|
24621
|
+
color = Color.offBlack64;
|
|
24622
|
+
} // define shape
|
|
24778
24623
|
|
|
24779
|
-
if (product === "sat") {
|
|
24780
|
-
return /*#__PURE__*/React.createElement(SATChoiceIcon, {
|
|
24781
|
-
letter: letter,
|
|
24782
|
-
a11yText: this.a11yText(letter),
|
|
24783
|
-
reviewMode: reviewMode,
|
|
24784
|
-
checked: checked,
|
|
24785
|
-
correct: correct,
|
|
24786
|
-
crossedOut: crossedOut
|
|
24787
|
-
});
|
|
24788
|
-
}
|
|
24789
24624
|
|
|
24790
|
-
|
|
24791
|
-
|
|
24792
|
-
|
|
24793
|
-
|
|
24794
|
-
|
|
24795
|
-
|
|
24796
|
-
pressed: pressed,
|
|
24797
|
-
focused: focused,
|
|
24798
|
-
correct: correct,
|
|
24799
|
-
showCorrectness: showCorrectness,
|
|
24800
|
-
primaryProductColor: primaryProductColor,
|
|
24801
|
-
previouslyAnswered: previouslyAnswered,
|
|
24802
|
-
transparentBackground: transparentBackground
|
|
24803
|
-
});
|
|
24625
|
+
var borderRadius;
|
|
24626
|
+
|
|
24627
|
+
if (multipleSelect) {
|
|
24628
|
+
borderRadius = 3;
|
|
24629
|
+
} else {
|
|
24630
|
+
borderRadius = LIBRARY_ICON_SIZE;
|
|
24804
24631
|
}
|
|
24805
24632
|
|
|
24633
|
+
return {
|
|
24634
|
+
backgroundColor,
|
|
24635
|
+
borderColor,
|
|
24636
|
+
color,
|
|
24637
|
+
borderRadius
|
|
24638
|
+
};
|
|
24806
24639
|
}
|
|
24807
24640
|
|
|
24808
|
-
|
|
24809
|
-
|
|
24810
|
-
|
|
24641
|
+
function LibraryChoiceIcon(props) {
|
|
24642
|
+
var {
|
|
24643
|
+
checked,
|
|
24644
|
+
crossedOut,
|
|
24645
|
+
showCorrectness,
|
|
24646
|
+
correct,
|
|
24647
|
+
focused,
|
|
24648
|
+
hovered,
|
|
24649
|
+
multipleSelect,
|
|
24650
|
+
primaryProductColor,
|
|
24651
|
+
previouslyAnswered,
|
|
24652
|
+
letter,
|
|
24653
|
+
pressed,
|
|
24654
|
+
transparentBackground
|
|
24655
|
+
} = props;
|
|
24656
|
+
var dynamicStyles = getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, primaryProductColor, correct, transparentBackground);
|
|
24657
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
24658
|
+
className: css(styles$x.iconWrapper)
|
|
24659
|
+
}, /*#__PURE__*/React.createElement(FocusRing, {
|
|
24660
|
+
color: primaryProductColor,
|
|
24661
|
+
visible: focused || hovered,
|
|
24662
|
+
multipleSelect: multipleSelect
|
|
24663
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
24664
|
+
style: dynamicStyles,
|
|
24665
|
+
"data-test-id": "choice-icon__library-choice-icon",
|
|
24666
|
+
className: 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
|
|
24667
|
+
// radio icon
|
|
24668
|
+
,
|
|
24669
|
+
"data-is-radio-icon": true
|
|
24670
|
+
}, /*#__PURE__*/React.createElement(ChoiceInner, {
|
|
24671
|
+
letter: letter,
|
|
24672
|
+
showCorrectness: showCorrectness,
|
|
24673
|
+
correct: correct
|
|
24674
|
+
}))), crossedOut && /*#__PURE__*/React.createElement(CrossOutLine, {
|
|
24675
|
+
color: dynamicStyles.borderColor
|
|
24676
|
+
}));
|
|
24677
|
+
}
|
|
24811
24678
|
|
|
24812
|
-
var styles$
|
|
24813
|
-
|
|
24814
|
-
display: "block",
|
|
24815
|
-
borderRadius: SAT_ICON_SIZE,
|
|
24816
|
-
borderStyle: "solid",
|
|
24817
|
-
borderWidth: 2,
|
|
24818
|
-
content: "''",
|
|
24819
|
-
height: SAT_ICON_SIZE,
|
|
24820
|
-
width: SAT_ICON_SIZE,
|
|
24821
|
-
top: 1,
|
|
24822
|
-
left: 1
|
|
24823
|
-
},
|
|
24824
|
-
iconWrapper: {
|
|
24825
|
-
display: "inline-block",
|
|
24826
|
-
position: "relative"
|
|
24827
|
-
},
|
|
24828
|
-
crossOutLine: {
|
|
24829
|
-
// Center the icon within the container.
|
|
24830
|
-
position: "absolute",
|
|
24831
|
-
top: "calc(50% - ".concat(CROSS_OUT_LINE_SIZE / 2, "px)"),
|
|
24832
|
-
left: "calc(50% - ".concat(CROSS_OUT_LINE_SIZE / 2, "px)")
|
|
24833
|
-
},
|
|
24834
|
-
libraryCircle: {
|
|
24679
|
+
var styles$v = StyleSheet.create({
|
|
24680
|
+
circle: {
|
|
24835
24681
|
// Make the circle
|
|
24836
24682
|
width: LIBRARY_ICON_SIZE,
|
|
24837
24683
|
height: LIBRARY_ICON_SIZE,
|
|
24838
24684
|
boxSizing: "border-box",
|
|
24839
|
-
borderRadius: LIBRARY_ICON_SIZE,
|
|
24840
24685
|
borderStyle: "solid",
|
|
24841
24686
|
borderWidth: 2,
|
|
24842
24687
|
// The default icons have letters in them. Style those letters.
|
|
@@ -24857,18 +24702,108 @@ var styles$t = StyleSheet.create({
|
|
|
24857
24702
|
// pixel so it is much better centered.
|
|
24858
24703
|
lineHeight: "1px"
|
|
24859
24704
|
},
|
|
24860
|
-
|
|
24705
|
+
circleCorrect: {
|
|
24861
24706
|
fontSize: LIBRARY_ICON_SIZE
|
|
24862
24707
|
},
|
|
24863
|
-
|
|
24708
|
+
circleIncorrect: {
|
|
24864
24709
|
fontSize: LIBRARY_ICON_SIZE,
|
|
24865
24710
|
borderColor: gray68,
|
|
24866
24711
|
color: gray68
|
|
24867
24712
|
},
|
|
24868
|
-
|
|
24713
|
+
circleIncorrectAnswered: {
|
|
24869
24714
|
backgroundColor: Color.red,
|
|
24870
24715
|
borderColor: Color.red,
|
|
24871
24716
|
color: Color.white
|
|
24717
|
+
}
|
|
24718
|
+
});
|
|
24719
|
+
|
|
24720
|
+
/* Component for rendering a letter icon in a SAT radio choice */
|
|
24721
|
+
|
|
24722
|
+
// TODO(amy): figure out a better scheme for specifying these
|
|
24723
|
+
// styles that isn't such a pain to grok. See some neat ideas
|
|
24724
|
+
// from MDR in https://phabricator.khanacademy.org/D35249.
|
|
24725
|
+
function constructSATStyles(reviewMode, correct, checked, multipleSelect) {
|
|
24726
|
+
var backgroundColor;
|
|
24727
|
+
var borderColor = satBlue;
|
|
24728
|
+
var color = satBlue;
|
|
24729
|
+
|
|
24730
|
+
if (reviewMode) {
|
|
24731
|
+
if (correct) {
|
|
24732
|
+
borderColor = satCorrectColor;
|
|
24733
|
+
color = checked ? white$1 : satCorrectColor;
|
|
24734
|
+
backgroundColor = checked ? satCorrectColor : white$1;
|
|
24735
|
+
} else if (checked) {
|
|
24736
|
+
borderColor = satIncorrectColor;
|
|
24737
|
+
color = white$1;
|
|
24738
|
+
backgroundColor = satIncorrectColor;
|
|
24739
|
+
}
|
|
24740
|
+
} else if (checked) {
|
|
24741
|
+
color = white$1;
|
|
24742
|
+
backgroundColor = satBlue;
|
|
24743
|
+
}
|
|
24744
|
+
|
|
24745
|
+
var borderRadius;
|
|
24746
|
+
|
|
24747
|
+
if (multipleSelect) {
|
|
24748
|
+
borderRadius = 3;
|
|
24749
|
+
} else {
|
|
24750
|
+
borderRadius = SAT_ICON_SIZE;
|
|
24751
|
+
}
|
|
24752
|
+
|
|
24753
|
+
return {
|
|
24754
|
+
color,
|
|
24755
|
+
backgroundColor,
|
|
24756
|
+
borderColor,
|
|
24757
|
+
borderRadius
|
|
24758
|
+
};
|
|
24759
|
+
}
|
|
24760
|
+
|
|
24761
|
+
function SATChoiceIcon(props) {
|
|
24762
|
+
var {
|
|
24763
|
+
letter,
|
|
24764
|
+
crossedOut,
|
|
24765
|
+
reviewMode,
|
|
24766
|
+
correct,
|
|
24767
|
+
checked,
|
|
24768
|
+
multipleSelect
|
|
24769
|
+
} = props;
|
|
24770
|
+
var {
|
|
24771
|
+
color,
|
|
24772
|
+
backgroundColor,
|
|
24773
|
+
borderColor,
|
|
24774
|
+
borderRadius
|
|
24775
|
+
} = constructSATStyles(reviewMode, correct, checked, multipleSelect);
|
|
24776
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
24777
|
+
className: css(styles$x.iconWrapper)
|
|
24778
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
24779
|
+
className: css(styles$u.satCircle),
|
|
24780
|
+
"data-test-id": "choice-icon__sat-choice-icon",
|
|
24781
|
+
style: {
|
|
24782
|
+
backgroundColor,
|
|
24783
|
+
borderColor,
|
|
24784
|
+
borderRadius
|
|
24785
|
+
}
|
|
24786
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
24787
|
+
style: {
|
|
24788
|
+
color
|
|
24789
|
+
},
|
|
24790
|
+
className: css(styles$u.letter)
|
|
24791
|
+
}, /*#__PURE__*/React.createElement("span", null, letter)), crossedOut && /*#__PURE__*/React.createElement(CrossOutLine, {
|
|
24792
|
+
color: borderColor,
|
|
24793
|
+
sat: true
|
|
24794
|
+
}));
|
|
24795
|
+
}
|
|
24796
|
+
|
|
24797
|
+
var styles$u = StyleSheet.create({
|
|
24798
|
+
satCircle: {
|
|
24799
|
+
display: "block",
|
|
24800
|
+
borderStyle: "solid",
|
|
24801
|
+
borderWidth: 2,
|
|
24802
|
+
content: "''",
|
|
24803
|
+
height: SAT_ICON_SIZE,
|
|
24804
|
+
width: SAT_ICON_SIZE,
|
|
24805
|
+
top: 1,
|
|
24806
|
+
left: 1
|
|
24872
24807
|
},
|
|
24873
24808
|
letter: {
|
|
24874
24809
|
// These properties make sure that this element has the exact
|
|
@@ -24888,6 +24823,55 @@ var styles$t = StyleSheet.create({
|
|
|
24888
24823
|
}
|
|
24889
24824
|
});
|
|
24890
24825
|
|
|
24826
|
+
/* Component for rendering a letter icon in radio choice */
|
|
24827
|
+
|
|
24828
|
+
function ChoiceIcon(props) {
|
|
24829
|
+
var {
|
|
24830
|
+
pos,
|
|
24831
|
+
reviewMode,
|
|
24832
|
+
checked,
|
|
24833
|
+
crossedOut,
|
|
24834
|
+
correct,
|
|
24835
|
+
product,
|
|
24836
|
+
multipleSelect,
|
|
24837
|
+
showCorrectness,
|
|
24838
|
+
focused,
|
|
24839
|
+
hovered,
|
|
24840
|
+
pressed,
|
|
24841
|
+
primaryProductColor = Color.blue,
|
|
24842
|
+
previouslyAnswered,
|
|
24843
|
+
transparentBackground
|
|
24844
|
+
} = props;
|
|
24845
|
+
var letter = getChoiceLetter(pos);
|
|
24846
|
+
|
|
24847
|
+
if (product === "sat") {
|
|
24848
|
+
return /*#__PURE__*/React.createElement(SATChoiceIcon, {
|
|
24849
|
+
letter: letter,
|
|
24850
|
+
reviewMode: reviewMode,
|
|
24851
|
+
checked: checked,
|
|
24852
|
+
correct: correct,
|
|
24853
|
+
crossedOut: crossedOut,
|
|
24854
|
+
multipleSelect: multipleSelect
|
|
24855
|
+
});
|
|
24856
|
+
}
|
|
24857
|
+
|
|
24858
|
+
return /*#__PURE__*/React.createElement(LibraryChoiceIcon, {
|
|
24859
|
+
letter: letter,
|
|
24860
|
+
reviewMode: reviewMode,
|
|
24861
|
+
checked: checked,
|
|
24862
|
+
crossedOut: crossedOut,
|
|
24863
|
+
focused: focused,
|
|
24864
|
+
hovered: hovered,
|
|
24865
|
+
pressed: pressed,
|
|
24866
|
+
correct: correct,
|
|
24867
|
+
showCorrectness: showCorrectness,
|
|
24868
|
+
primaryProductColor: primaryProductColor,
|
|
24869
|
+
previouslyAnswered: previouslyAnswered,
|
|
24870
|
+
transparentBackground: transparentBackground,
|
|
24871
|
+
multipleSelect: multipleSelect
|
|
24872
|
+
});
|
|
24873
|
+
}
|
|
24874
|
+
|
|
24891
24875
|
/**
|
|
24892
24876
|
* Renders text indicating whether the choice was correct or
|
|
24893
24877
|
* not and whether the choice was selected or not.
|
|
@@ -24896,63 +24880,68 @@ var styles$t = StyleSheet.create({
|
|
|
24896
24880
|
* between the states more immediately clear to users.
|
|
24897
24881
|
*/
|
|
24898
24882
|
|
|
24899
|
-
|
|
24900
|
-
|
|
24901
|
-
|
|
24902
|
-
|
|
24903
|
-
|
|
24904
|
-
|
|
24905
|
-
}
|
|
24883
|
+
function renderText(checked, correct, crossedOut) {
|
|
24884
|
+
if (correct) {
|
|
24885
|
+
// For correct answers, we surface checked _or_ crossedOut state,
|
|
24886
|
+
// because any interaction with the correct answer is noteworthy!
|
|
24887
|
+
if (checked) {
|
|
24888
|
+
return i18n._("Correct (selected)");
|
|
24889
|
+
}
|
|
24906
24890
|
|
|
24907
|
-
if (
|
|
24908
|
-
|
|
24909
|
-
|
|
24910
|
-
|
|
24911
|
-
|
|
24912
|
-
|
|
24891
|
+
if (crossedOut) {
|
|
24892
|
+
return i18n._("Correct (but you crossed it out)");
|
|
24893
|
+
}
|
|
24894
|
+
|
|
24895
|
+
return i18n._("Correct");
|
|
24896
|
+
} // But, for incorrect answers, we only surface checked state,
|
|
24897
|
+
// because crossing out an incorrect answer is not noteworthy.
|
|
24913
24898
|
|
|
24914
|
-
if (crossedOut) {
|
|
24915
|
-
return i18n._("Correct (but you crossed it out)");
|
|
24916
|
-
}
|
|
24917
24899
|
|
|
24918
|
-
|
|
24919
|
-
|
|
24920
|
-
|
|
24900
|
+
if (checked) {
|
|
24901
|
+
return i18n._("Incorrect (selected)");
|
|
24902
|
+
}
|
|
24921
24903
|
|
|
24904
|
+
return i18n._("Incorrect");
|
|
24905
|
+
}
|
|
24906
|
+
|
|
24907
|
+
function OptionStatus(props) {
|
|
24908
|
+
var {
|
|
24909
|
+
checked,
|
|
24910
|
+
correct,
|
|
24911
|
+
crossedOut,
|
|
24912
|
+
previouslyAnswered,
|
|
24913
|
+
reviewMode,
|
|
24914
|
+
satStyling
|
|
24915
|
+
} = props; // Option status is exclued for SAT
|
|
24916
|
+
|
|
24917
|
+
if (satStyling) {
|
|
24918
|
+
return null;
|
|
24919
|
+
} // Option status is shown only in review mode, or for incorrectly
|
|
24920
|
+
// answered items.
|
|
24922
24921
|
|
|
24923
|
-
if (checked) {
|
|
24924
|
-
return i18n._("Incorrect (selected)");
|
|
24925
|
-
}
|
|
24926
24922
|
|
|
24927
|
-
|
|
24923
|
+
if (!reviewMode && !previouslyAnswered) {
|
|
24924
|
+
return null;
|
|
24928
24925
|
}
|
|
24929
24926
|
|
|
24930
|
-
|
|
24931
|
-
var {
|
|
24932
|
-
checked,
|
|
24933
|
-
correct,
|
|
24934
|
-
previouslyAnswered
|
|
24935
|
-
} = this.props;
|
|
24936
|
-
var textStyle;
|
|
24927
|
+
var textStyle;
|
|
24937
24928
|
|
|
24938
|
-
|
|
24939
|
-
|
|
24929
|
+
if (correct) {
|
|
24930
|
+
textStyle = styles$t.correct;
|
|
24931
|
+
} else {
|
|
24932
|
+
if (checked || previouslyAnswered) {
|
|
24933
|
+
textStyle = styles$t.incorrectAnswered;
|
|
24940
24934
|
} else {
|
|
24941
|
-
|
|
24942
|
-
textStyle = styles$s.incorrectAnswered;
|
|
24943
|
-
} else {
|
|
24944
|
-
textStyle = styles$s.incorrect;
|
|
24945
|
-
}
|
|
24935
|
+
textStyle = styles$t.incorrect;
|
|
24946
24936
|
}
|
|
24947
|
-
|
|
24948
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
24949
|
-
className: css(styles$s.text, textStyle)
|
|
24950
|
-
}, this._renderText());
|
|
24951
24937
|
}
|
|
24952
24938
|
|
|
24939
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
24940
|
+
className: css(styles$t.text, textStyle)
|
|
24941
|
+
}, renderText(checked, correct, crossedOut));
|
|
24953
24942
|
}
|
|
24954
24943
|
|
|
24955
|
-
var styles$
|
|
24944
|
+
var styles$t = StyleSheet.create({
|
|
24956
24945
|
text: {
|
|
24957
24946
|
alignItems: "center",
|
|
24958
24947
|
display: "flex",
|
|
@@ -24978,317 +24967,236 @@ var ellipsisHorizontalIcon = {
|
|
|
24978
24967
|
width: 100,
|
|
24979
24968
|
height: 27.284
|
|
24980
24969
|
};
|
|
24970
|
+
// Note(TB): Received errors when using useUniqueIdWithMock
|
|
24971
|
+
// so created this workaround function. Will update when
|
|
24972
|
+
// useUniqueIdWithMock is available.
|
|
24973
|
+
// https://khanacademy.atlassian.net/browse/FEI-4861?atlOrigin=eyJpIjoiNDJlZWMwNjM1NWJhNDBkMWFjY2FmN2I0ZjcxZmQxOGUiLCJwIjoiaiJ9
|
|
24974
|
+
var id = 0;
|
|
24981
24975
|
|
|
24982
|
-
|
|
24983
|
-
|
|
24984
|
-
|
|
24985
|
-
|
|
24986
|
-
_defineProperty(this, "justFinishedTouch", void 0);
|
|
24987
|
-
|
|
24988
|
-
_defineProperty(this, "justFinishedTimeoutID", void 0);
|
|
24989
|
-
|
|
24990
|
-
_defineProperty(this, "_input", void 0);
|
|
24991
|
-
|
|
24992
|
-
_defineProperty(this, "state", {
|
|
24993
|
-
isInputFocused: false,
|
|
24994
|
-
isInputActive: false
|
|
24995
|
-
});
|
|
24996
|
-
|
|
24997
|
-
_defineProperty(this, "inputRef", ref => {
|
|
24998
|
-
this._input = ref;
|
|
24999
|
-
});
|
|
25000
|
-
|
|
25001
|
-
_defineProperty(this, "renderOptionStatus", () => {
|
|
25002
|
-
var {
|
|
25003
|
-
correct,
|
|
25004
|
-
checked,
|
|
25005
|
-
crossedOut,
|
|
25006
|
-
reviewMode,
|
|
25007
|
-
previouslyAnswered
|
|
25008
|
-
} = this.props; // Option status is exclued for SAT
|
|
25009
|
-
|
|
25010
|
-
if (this.props.apiOptions.satStyling) {
|
|
25011
|
-
return null;
|
|
25012
|
-
} // Option status is shown only in review mode, or for incorrectly
|
|
25013
|
-
// answered items.
|
|
24976
|
+
function uniqueId() {
|
|
24977
|
+
return "choice-".concat(id++);
|
|
24978
|
+
}
|
|
25014
24979
|
|
|
24980
|
+
function Choice(props) {
|
|
24981
|
+
var {
|
|
24982
|
+
disabled,
|
|
24983
|
+
checked,
|
|
24984
|
+
content,
|
|
24985
|
+
crossedOut,
|
|
24986
|
+
showCorrectness,
|
|
24987
|
+
multipleSelect,
|
|
24988
|
+
onChange,
|
|
24989
|
+
reviewMode,
|
|
24990
|
+
correct,
|
|
24991
|
+
apiOptions,
|
|
24992
|
+
previouslyAnswered,
|
|
24993
|
+
pos,
|
|
24994
|
+
showRationale,
|
|
24995
|
+
rationale,
|
|
24996
|
+
forwardedRef
|
|
24997
|
+
} = props;
|
|
24998
|
+
var [isInputFocused, setIsInputFocused] = useState(false);
|
|
24999
|
+
useEffect(() => {
|
|
25000
|
+
if (isInputFocused && disabled) {
|
|
25001
|
+
setIsInputFocused(false);
|
|
25002
|
+
}
|
|
25003
|
+
}, [disabled, isInputFocused, setIsInputFocused]); // Call `this.props.onChange` with the given values. Any keys that are not
|
|
25004
|
+
// specified will be filled in with the current value. (For example, if
|
|
25005
|
+
// `checked` is specified but `crossedOut` is not, then `crossedOut` will
|
|
25006
|
+
// be filled in with `this.props.crossedOut`.)
|
|
25007
|
+
//
|
|
25008
|
+
// This enables us to use shorthand inside this component, while
|
|
25009
|
+
// maintaining a consistent API for the parent.
|
|
25015
25010
|
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
}
|
|
25011
|
+
function sendChange(newValues) {
|
|
25012
|
+
var _newValues$checked, _newValues$crossedOut;
|
|
25019
25013
|
|
|
25020
|
-
|
|
25021
|
-
|
|
25022
|
-
|
|
25023
|
-
|
|
25024
|
-
|
|
25025
|
-
});
|
|
25014
|
+
var updatedChecked = (_newValues$checked = newValues.checked) !== null && _newValues$checked !== void 0 ? _newValues$checked : checked;
|
|
25015
|
+
var updatedCrossedOut = (_newValues$crossedOut = newValues.crossedOut) !== null && _newValues$crossedOut !== void 0 ? _newValues$crossedOut : crossedOut;
|
|
25016
|
+
onChange({
|
|
25017
|
+
checked: updatedChecked,
|
|
25018
|
+
crossedOut: updatedCrossedOut
|
|
25026
25019
|
});
|
|
25020
|
+
} // HACK: while most of the styling for rendering SAT items is handled
|
|
25021
|
+
// via aphrodite, we also need to assign normal CSS classnames here to
|
|
25022
|
+
// special-case the coloring of MathJax formulas (see .MathJax .math in
|
|
25023
|
+
// stylesheets/task-package/tasks.less)
|
|
25027
25024
|
|
|
25028
|
-
_defineProperty(this, "renderChoiceIcon", _ref => {
|
|
25029
|
-
var {
|
|
25030
|
-
isFocused,
|
|
25031
|
-
isPressed
|
|
25032
|
-
} = _ref;
|
|
25033
|
-
var {
|
|
25034
|
-
radioStyleVersion,
|
|
25035
|
-
primaryProductColor
|
|
25036
|
-
} = this.props.apiOptions.styling;
|
|
25037
|
-
var finalStyles = typeof radioStyleVersion === "undefined" ? false : radioStyleVersion === "final";
|
|
25038
|
-
|
|
25039
|
-
if (!finalStyles && !this.props.apiOptions.satStyling) {
|
|
25040
|
-
return null;
|
|
25041
|
-
}
|
|
25042
25025
|
|
|
25043
|
-
|
|
25044
|
-
|
|
25045
|
-
|
|
25046
|
-
|
|
25047
|
-
|
|
25048
|
-
|
|
25049
|
-
|
|
25050
|
-
showCorrectness: this.props.showCorrectness,
|
|
25051
|
-
reviewMode: this.props.reviewMode,
|
|
25052
|
-
product: this.props.apiOptions.satStyling ? "sat" : "library",
|
|
25053
|
-
primaryProductColor: primaryProductColor,
|
|
25054
|
-
previouslyAnswered: this.props.previouslyAnswered
|
|
25055
|
-
});
|
|
25056
|
-
});
|
|
25057
|
-
|
|
25058
|
-
_defineProperty(this, "_sendChange", newValues => {
|
|
25059
|
-
var _newValues$checked, _newValues$crossedOut;
|
|
25026
|
+
var satCorrectChoice = apiOptions.satStyling && reviewMode && correct;
|
|
25027
|
+
var satIncorrectChecked = apiOptions.satStyling && reviewMode && !correct && checked;
|
|
25028
|
+
var descriptionClassName = classNames("description", satCorrectChoice && "sat-correct", satIncorrectChecked && "sat-incorrect", css(!apiOptions.satStyling && styles$s.description, apiOptions.satStyling && styles$s.satDescription, satCorrectChoice && styles$s.satDescriptionCorrect, satCorrectChoice && checked && styles$s.satDescriptionCorrectChecked, satIncorrectChecked && styles$s.satDescriptionIncorrectChecked));
|
|
25029
|
+
var rationaleClassName = classNames("perseus-radio-rationale-content", 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
|
|
25030
|
+
// disabled. However, we don't want to do this in the SAT product and
|
|
25031
|
+
// we also don't want to do this when we're in review mode in the
|
|
25032
|
+
// content library.
|
|
25060
25033
|
|
|
25061
|
-
|
|
25062
|
-
|
|
25063
|
-
|
|
25064
|
-
|
|
25065
|
-
|
|
25034
|
+
var showDimmed = !apiOptions.satStyling && !reviewMode && apiOptions.readOnly || crossedOut;
|
|
25035
|
+
var letter = getChoiceLetter(pos);
|
|
25036
|
+
var a11yText = getA11yText(letter, checked, correct, crossedOut, showCorrectness);
|
|
25037
|
+
var choiceId = uniqueId();
|
|
25038
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
25039
|
+
style: {
|
|
25040
|
+
dispay: "flex",
|
|
25041
|
+
flexDirection: "column"
|
|
25042
|
+
}
|
|
25043
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
25044
|
+
style: {
|
|
25045
|
+
display: "flex",
|
|
25046
|
+
flexDirection: "row",
|
|
25047
|
+
width: "100%",
|
|
25048
|
+
opacity: showDimmed ? 0.5 : 1.0
|
|
25049
|
+
}
|
|
25050
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
25051
|
+
className: "perseus-sr-only"
|
|
25052
|
+
}, /*#__PURE__*/React.createElement("input", {
|
|
25053
|
+
type: multipleSelect ? "checkbox" : "radio",
|
|
25054
|
+
id: choiceId,
|
|
25055
|
+
checked: checked,
|
|
25056
|
+
onClick: () => {
|
|
25057
|
+
// If we're checking a crossed-out option, let's
|
|
25058
|
+
// also uncross it.
|
|
25059
|
+
sendChange({
|
|
25060
|
+
checked: !checked,
|
|
25061
|
+
crossedOut: false
|
|
25066
25062
|
});
|
|
25067
|
-
}
|
|
25068
|
-
|
|
25069
|
-
|
|
25070
|
-
|
|
25071
|
-
|
|
25072
|
-
|
|
25073
|
-
|
|
25074
|
-
|
|
25075
|
-
|
|
25076
|
-
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
|
|
25063
|
+
},
|
|
25064
|
+
onChange: () => {},
|
|
25065
|
+
disabled: disabled || apiOptions.staticRender || apiOptions.readOnly,
|
|
25066
|
+
tabIndex: -1
|
|
25067
|
+
}), /*#__PURE__*/React.createElement("label", {
|
|
25068
|
+
htmlFor: choiceId
|
|
25069
|
+
}, a11yText, " \xA0 ", content)), /*#__PURE__*/React.createElement(Clickable, {
|
|
25070
|
+
onClick: () => {
|
|
25071
|
+
// If we're checking a crossed-out option, let's
|
|
25072
|
+
// also uncross it.
|
|
25073
|
+
sendChange({
|
|
25074
|
+
checked: !checked,
|
|
25075
|
+
crossedOut: false
|
|
25080
25076
|
});
|
|
25081
|
-
}
|
|
25082
|
-
|
|
25077
|
+
},
|
|
25078
|
+
className: descriptionClassName,
|
|
25079
|
+
disabled: disabled || apiOptions.staticRender || apiOptions.readOnly,
|
|
25080
|
+
style: {
|
|
25081
|
+
flex: 1
|
|
25082
|
+
},
|
|
25083
|
+
ref: forwardedRef,
|
|
25084
|
+
"aria-hidden": "true"
|
|
25085
|
+
}, _ref => {
|
|
25086
|
+
var _apiOptions$styling;
|
|
25083
25087
|
|
|
25084
|
-
render() {
|
|
25085
|
-
var sat = this.props.apiOptions.satStyling;
|
|
25086
25088
|
var {
|
|
25087
|
-
|
|
25088
|
-
|
|
25089
|
-
|
|
25090
|
-
} =
|
|
25091
|
-
// via aphrodite, we also need to assign normal CSS classnames here to
|
|
25092
|
-
// special-case the coloring of MathJax formulas (see .MathJax .math in
|
|
25093
|
-
// stylesheets/task-package/tasks.less)
|
|
25094
|
-
|
|
25095
|
-
var satCorrectChoice = sat && reviewMode && correct;
|
|
25096
|
-
var satIncorrectChecked = sat && reviewMode && !correct && checked;
|
|
25097
|
-
var descriptionClassName = classNames("description", satCorrectChoice && "sat-correct", satIncorrectChecked && "sat-incorrect", css(!sat && styles$r.description, sat && styles$r.satDescription, satCorrectChoice && styles$r.satDescriptionCorrect, satCorrectChoice && checked && styles$r.satDescriptionCorrectChecked, satIncorrectChecked && styles$r.satDescriptionIncorrectChecked));
|
|
25098
|
-
var rationaleClassName = classNames("perseus-radio-rationale-content", 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
|
|
25099
|
-
// disabled. However, we don't want to do this in the SAT product and
|
|
25100
|
-
// we also don't want to do this when we're in review mode in the
|
|
25101
|
-
// content library.
|
|
25102
|
-
|
|
25103
|
-
var showDimmed = !sat && !reviewMode && this.props.apiOptions.readOnly || this.props.crossedOut;
|
|
25089
|
+
hovered,
|
|
25090
|
+
focused,
|
|
25091
|
+
pressed
|
|
25092
|
+
} = _ref;
|
|
25104
25093
|
return /*#__PURE__*/React.createElement("div", {
|
|
25105
|
-
style: {
|
|
25106
|
-
dispay: "flex",
|
|
25107
|
-
flexDirection: "column"
|
|
25108
|
-
}
|
|
25109
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
25110
25094
|
style: {
|
|
25111
25095
|
display: "flex",
|
|
25112
25096
|
flexDirection: "row",
|
|
25113
|
-
|
|
25114
|
-
|
|
25097
|
+
justifyContent: "center",
|
|
25098
|
+
alignContent: "center",
|
|
25099
|
+
paddingTop: Spacing.xSmall_8,
|
|
25100
|
+
paddingBottom: Spacing.xSmall_8
|
|
25115
25101
|
}
|
|
25116
|
-
}, /*#__PURE__*/React.createElement(
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
25124
|
-
|
|
25125
|
-
|
|
25126
|
-
|
|
25127
|
-
|
|
25128
|
-
|
|
25129
|
-
|
|
25102
|
+
}, /*#__PURE__*/React.createElement(ChoiceIcon, {
|
|
25103
|
+
pos: pos,
|
|
25104
|
+
correct: correct,
|
|
25105
|
+
crossedOut: crossedOut,
|
|
25106
|
+
pressed: pressed,
|
|
25107
|
+
focused: focused,
|
|
25108
|
+
checked: checked,
|
|
25109
|
+
hovered: hovered,
|
|
25110
|
+
showCorrectness: showCorrectness,
|
|
25111
|
+
multipleSelect: multipleSelect,
|
|
25112
|
+
reviewMode: reviewMode,
|
|
25113
|
+
product: apiOptions.satStyling ? "sat" : "library",
|
|
25114
|
+
primaryProductColor: (_apiOptions$styling = apiOptions.styling) === null || _apiOptions$styling === void 0 ? void 0 : _apiOptions$styling.primaryProductColor,
|
|
25115
|
+
previouslyAnswered: previouslyAnswered
|
|
25116
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
25130
25117
|
style: {
|
|
25131
|
-
|
|
25118
|
+
paddingLeft: Spacing.small_12,
|
|
25119
|
+
textAlign: "left",
|
|
25120
|
+
flex: 1,
|
|
25121
|
+
paddingTop: 4
|
|
25132
25122
|
}
|
|
25133
|
-
},
|
|
25123
|
+
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(OptionStatus, {
|
|
25124
|
+
checked: checked,
|
|
25125
|
+
correct: correct,
|
|
25126
|
+
crossedOut: crossedOut,
|
|
25127
|
+
previouslyAnswered: previouslyAnswered,
|
|
25128
|
+
reviewMode: reviewMode,
|
|
25129
|
+
satStyling: apiOptions.satStyling
|
|
25130
|
+
})), /*#__PURE__*/React.createElement("div", null, content)));
|
|
25131
|
+
}), apiOptions.crossOutEnabled && !reviewMode && /*#__PURE__*/React.createElement(Popover, {
|
|
25132
|
+
dismissEnabled: true,
|
|
25133
|
+
content: _ref2 => {
|
|
25134
25134
|
var {
|
|
25135
|
-
|
|
25136
|
-
focused,
|
|
25137
|
-
pressed
|
|
25135
|
+
close
|
|
25138
25136
|
} = _ref2;
|
|
25139
|
-
return /*#__PURE__*/React.createElement(
|
|
25140
|
-
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
|
|
25153
|
-
|
|
25154
|
-
|
|
25155
|
-
|
|
25156
|
-
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
close
|
|
25164
|
-
} = _ref3;
|
|
25165
|
-
return /*#__PURE__*/React.createElement(PopoverContent, {
|
|
25166
|
-
title: "Cross out",
|
|
25167
|
-
content: "Cross out option",
|
|
25168
|
-
closeButtonVisible: true,
|
|
25169
|
-
actions: /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Strut, {
|
|
25170
|
-
size: Spacing.medium_16
|
|
25171
|
-
}), /*#__PURE__*/React.createElement(Button, {
|
|
25172
|
-
kind: "primary",
|
|
25173
|
-
"aria-label": "Cross out Choice ".concat(getChoiceLetter(this.props.pos)),
|
|
25174
|
-
disabled: this.props.apiOptions.readOnly || reviewMode,
|
|
25175
|
-
onClick: () => {
|
|
25176
|
-
if (!this.props.crossedOut) {
|
|
25177
|
-
// If we're crossing
|
|
25178
|
-
// out a checked
|
|
25179
|
-
// option, let's also
|
|
25180
|
-
// uncheck it.
|
|
25181
|
-
this._sendChange({
|
|
25182
|
-
checked: false,
|
|
25183
|
-
crossedOut: true
|
|
25184
|
-
});
|
|
25185
|
-
} else {
|
|
25186
|
-
this._sendChange({
|
|
25187
|
-
crossedOut: false
|
|
25188
|
-
});
|
|
25189
|
-
}
|
|
25190
|
-
|
|
25191
|
-
close();
|
|
25137
|
+
return /*#__PURE__*/React.createElement(PopoverContent, {
|
|
25138
|
+
title: "Cross out",
|
|
25139
|
+
content: "Cross out option",
|
|
25140
|
+
closeButtonVisible: true,
|
|
25141
|
+
actions: /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Strut, {
|
|
25142
|
+
size: Spacing.medium_16
|
|
25143
|
+
}), /*#__PURE__*/React.createElement(Button, {
|
|
25144
|
+
kind: "primary",
|
|
25145
|
+
"aria-label": "Cross out Choice ".concat(getChoiceLetter(pos)),
|
|
25146
|
+
disabled: apiOptions.readOnly || reviewMode,
|
|
25147
|
+
onClick: () => {
|
|
25148
|
+
if (!crossedOut) {
|
|
25149
|
+
// If we're crossing
|
|
25150
|
+
// out a checked
|
|
25151
|
+
// option, let's also
|
|
25152
|
+
// uncheck it.
|
|
25153
|
+
sendChange({
|
|
25154
|
+
checked: false,
|
|
25155
|
+
crossedOut: true
|
|
25156
|
+
});
|
|
25157
|
+
} else {
|
|
25158
|
+
sendChange({
|
|
25159
|
+
crossedOut: false
|
|
25160
|
+
});
|
|
25192
25161
|
}
|
|
25193
|
-
|
|
25194
|
-
|
|
25195
|
-
|
|
25162
|
+
|
|
25163
|
+
close();
|
|
25164
|
+
}
|
|
25165
|
+
}, crossedOut ? "Bring back" : "Cross out"))
|
|
25166
|
+
});
|
|
25167
|
+
}
|
|
25168
|
+
}, _ref3 => {
|
|
25169
|
+
var {
|
|
25170
|
+
open
|
|
25171
|
+
} = _ref3;
|
|
25172
|
+
return /*#__PURE__*/React.createElement(Clickable, {
|
|
25173
|
+
onClick: open,
|
|
25174
|
+
"aria-label": "Open menu for Choice ".concat(getChoiceLetter(pos)),
|
|
25175
|
+
disabled: apiOptions.staticRender
|
|
25196
25176
|
}, _ref4 => {
|
|
25197
|
-
|
|
25198
|
-
|
|
25199
|
-
|
|
25200
|
-
|
|
25201
|
-
onClick: open,
|
|
25202
|
-
"aria-label": "Open menu for Choice ".concat(getChoiceLetter(this.props.pos)),
|
|
25203
|
-
disabled: this.props.apiOptions.staticRender
|
|
25204
|
-
}, _ref5 => {
|
|
25205
|
-
return /*#__PURE__*/React.createElement(Icon, {
|
|
25206
|
-
icon: ellipsisHorizontalIcon,
|
|
25207
|
-
size: 3,
|
|
25208
|
-
color: Color.offBlack64
|
|
25209
|
-
});
|
|
25177
|
+
return /*#__PURE__*/React.createElement(Icon, {
|
|
25178
|
+
icon: ellipsisHorizontalIcon,
|
|
25179
|
+
size: 3,
|
|
25180
|
+
color: Color.offBlack64
|
|
25210
25181
|
});
|
|
25211
|
-
})
|
|
25212
|
-
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
}
|
|
25216
|
-
|
|
25182
|
+
});
|
|
25183
|
+
})), showRationale && /*#__PURE__*/React.createElement("div", {
|
|
25184
|
+
className: rationaleClassName,
|
|
25185
|
+
"data-test-id": "perseus-radio-rationale-content-".concat(pos)
|
|
25186
|
+
}, rationale));
|
|
25217
25187
|
}
|
|
25218
25188
|
|
|
25219
|
-
|
|
25220
|
-
|
|
25221
|
-
// This change will also require make sure that item-renderer.jsx and
|
|
25222
|
-
// server-item-renderer.jsx have appropriate defaults for apiOptions
|
|
25223
|
-
// because many of the properties on Options.propTypes are required.
|
|
25224
|
-
apiOptions: PropTypes.shape({
|
|
25225
|
-
satStyling: PropTypes.bool,
|
|
25226
|
-
isMobile: PropTypes.bool,
|
|
25227
|
-
styling: PropTypes.shape({
|
|
25228
|
-
radioStyleVersion: PropTypes.oneOf(["intermediate", "final"]),
|
|
25229
|
-
primaryProductColor: PropTypes.string
|
|
25230
|
-
}),
|
|
25231
|
-
readOnly: PropTypes.bool
|
|
25232
|
-
}),
|
|
25233
|
-
checked: PropTypes.bool,
|
|
25234
|
-
className: PropTypes.string,
|
|
25235
|
-
rationale: PropTypes.node,
|
|
25236
|
-
content: PropTypes.node,
|
|
25237
|
-
correct: PropTypes.bool,
|
|
25238
|
-
deselectEnabled: PropTypes.bool,
|
|
25239
|
-
disabled: PropTypes.bool,
|
|
25240
|
-
editMode: PropTypes.bool,
|
|
25241
|
-
groupName: PropTypes.string,
|
|
25242
|
-
isLastChoice: PropTypes.bool,
|
|
25243
|
-
// Needed for border styling
|
|
25244
|
-
// This indicates the position of the choice relative to others
|
|
25245
|
-
// (so that we can display a nice little (A), (B), etc. next to it)
|
|
25246
|
-
// Also used to generate an id for each input.
|
|
25247
|
-
pos: PropTypes.number,
|
|
25248
|
-
reviewMode: PropTypes.bool,
|
|
25249
|
-
showRationale: PropTypes.bool,
|
|
25250
|
-
showCorrectness: PropTypes.bool,
|
|
25251
|
-
type: PropTypes.string,
|
|
25252
|
-
// Indicates whether the user has "crossed out" this choice, meaning
|
|
25253
|
-
// that they don't think it's correct. This value does not affect
|
|
25254
|
-
// scoring or other behavior; it's just a note for the user's
|
|
25255
|
-
// reference.
|
|
25256
|
-
crossedOut: PropTypes.bool,
|
|
25257
|
-
// Indicates that the user has previously selected this answer. These
|
|
25258
|
-
// answers may be rendered orange in review, rather than grey if
|
|
25259
|
-
// incorrect.
|
|
25260
|
-
previouslyAnswered: PropTypes.bool,
|
|
25261
|
-
// A callback indicating that this choice has changed. Its argument is
|
|
25262
|
-
// an object with two keys: `checked` and `crossedOut`. Each contains a
|
|
25263
|
-
// boolean value specifying the new checked and crossed-out value of
|
|
25264
|
-
// this choice.
|
|
25265
|
-
onChange: PropTypes.func,
|
|
25266
|
-
// Callbacks for when the user presses the up arrow or down arrow
|
|
25267
|
-
// (respectively) to navigate between choices. When called, the parent
|
|
25268
|
-
// finds the prev/next choice, focuses the input, and checks the radio
|
|
25269
|
-
// button if appropriate. (The reason we have custom behavior is that
|
|
25270
|
-
// we actually _don't_ want to auto-select crossed-out choices!)
|
|
25271
|
-
goToPrevChoice: PropTypes.func.isRequired,
|
|
25272
|
-
goToNextChoice: PropTypes.func.isRequired
|
|
25273
|
-
});
|
|
25274
|
-
|
|
25275
|
-
_defineProperty(Choice, "defaultProps", {
|
|
25276
|
-
apiOptions: {
|
|
25277
|
-
styling: {}
|
|
25278
|
-
},
|
|
25189
|
+
Choice.defaultProps = {
|
|
25190
|
+
disabled: false,
|
|
25279
25191
|
checked: false,
|
|
25280
|
-
|
|
25192
|
+
onChange: newValues => {},
|
|
25281
25193
|
correct: false,
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
onChange: function onChange() {},
|
|
25285
|
-
showRationale: false,
|
|
25286
|
-
type: "radio",
|
|
25194
|
+
apiOptions: ApiOptions.defaults,
|
|
25195
|
+
previouslyAnswered: false,
|
|
25287
25196
|
pos: 0,
|
|
25288
|
-
|
|
25289
|
-
}
|
|
25290
|
-
|
|
25291
|
-
var styles$r = StyleSheet.create({
|
|
25197
|
+
showRationale: false
|
|
25198
|
+
};
|
|
25199
|
+
var styles$s = StyleSheet.create({
|
|
25292
25200
|
description: {
|
|
25293
25201
|
display: "inline-block",
|
|
25294
25202
|
width: "100%"
|
|
@@ -25328,185 +25236,87 @@ var styles$r = StyleSheet.create({
|
|
|
25328
25236
|
marginLeft: 45
|
|
25329
25237
|
}
|
|
25330
25238
|
});
|
|
25239
|
+
var Choice$1 = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.createElement(Choice, _extends({}, props, {
|
|
25240
|
+
forwardedRef: ref
|
|
25241
|
+
})));
|
|
25331
25242
|
|
|
25332
|
-
|
|
25333
|
-
constructor() {
|
|
25334
|
-
super(...arguments);
|
|
25335
|
-
|
|
25336
|
-
_defineProperty(this, "_choice", void 0);
|
|
25337
|
-
|
|
25338
|
-
_defineProperty(this, "focusInput", () => {
|
|
25339
|
-
this._choice.focusInput();
|
|
25340
|
-
});
|
|
25341
|
-
}
|
|
25243
|
+
var _excluded$2 = ["showContent", "content", "forwardedRef"];
|
|
25342
25244
|
|
|
25343
|
-
|
|
25344
|
-
|
|
25345
|
-
|
|
25346
|
-
|
|
25347
|
-
|
|
25348
|
-
|
|
25349
|
-
|
|
25350
|
-
|
|
25351
|
-
|
|
25352
|
-
|
|
25353
|
-
|
|
25354
|
-
|
|
25355
|
-
|
|
25356
|
-
|
|
25357
|
-
|
|
25358
|
-
|
|
25359
|
-
|
|
25360
|
-
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25245
|
+
function ChoiceNoneAbove(props) {
|
|
25246
|
+
var {
|
|
25247
|
+
showContent,
|
|
25248
|
+
content,
|
|
25249
|
+
forwardedRef
|
|
25250
|
+
} = props,
|
|
25251
|
+
rest = _objectWithoutProperties(props, _excluded$2);
|
|
25252
|
+
|
|
25253
|
+
var choiceProps = _objectSpread2(_objectSpread2({}, rest), {}, {
|
|
25254
|
+
content: showContent ? content :
|
|
25255
|
+
/*#__PURE__*/
|
|
25256
|
+
// We use a Renderer here because that is how
|
|
25257
|
+
// `this.props.content` is wrapped otherwise.
|
|
25258
|
+
// We pass in a key here so that we avoid a semi-spurious
|
|
25259
|
+
// react warning when we render this in the same place
|
|
25260
|
+
// as the previous choice content renderer.
|
|
25261
|
+
// Note this destroys state, but since all we're doing
|
|
25262
|
+
// is outputting "None of the above", that is okay.
|
|
25263
|
+
//
|
|
25264
|
+
// todo(matthewc): this seems like way overkill
|
|
25265
|
+
// just to render a string
|
|
25266
|
+
React.createElement(Renderer, {
|
|
25267
|
+
key: "noneOfTheAboveRenderer",
|
|
25268
|
+
content: i18n._("None of the above")
|
|
25269
|
+
})
|
|
25270
|
+
});
|
|
25364
25271
|
|
|
25272
|
+
return /*#__PURE__*/React.createElement(Choice$1, _extends({}, choiceProps, {
|
|
25273
|
+
ref: forwardedRef
|
|
25274
|
+
}));
|
|
25365
25275
|
}
|
|
25366
25276
|
|
|
25367
|
-
|
|
25368
|
-
className: PropTypes.string,
|
|
25369
|
-
content: PropTypes.node,
|
|
25370
|
-
showContent: PropTypes.bool
|
|
25371
|
-
});
|
|
25372
|
-
|
|
25373
|
-
_defineProperty(ChoiceNoneAbove, "defaultProps", {
|
|
25277
|
+
ChoiceNoneAbove.defaultProps = {
|
|
25374
25278
|
showContent: true
|
|
25375
|
-
}
|
|
25279
|
+
};
|
|
25280
|
+
var ChoiceNoneAbove$1 = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.createElement(ChoiceNoneAbove, _extends({}, props, {
|
|
25281
|
+
forwardedRef: ref
|
|
25282
|
+
})));
|
|
25376
25283
|
|
|
25377
25284
|
var {
|
|
25378
25285
|
captureScratchpadTouchStart: captureScratchpadTouchStart$2
|
|
25379
|
-
} = Util;
|
|
25380
|
-
var ChoicesType = PropTypes.arrayOf(PropTypes.shape({
|
|
25381
|
-
// Indicates whether this choice is checked.
|
|
25382
|
-
checked: PropTypes.bool,
|
|
25383
|
-
// Indicates whether the user has "crossed out" this choice, meaning
|
|
25384
|
-
// that they don't think it's correct. This value does not affect
|
|
25385
|
-
// scoring or other behavior; it's just a note for the user's
|
|
25386
|
-
// reference.
|
|
25387
|
-
crossedOut: PropTypes.bool,
|
|
25388
|
-
content: PropTypes.node,
|
|
25389
|
-
rationale: PropTypes.node,
|
|
25390
|
-
hasRationale: PropTypes.bool,
|
|
25391
|
-
showRationale: PropTypes.bool,
|
|
25392
|
-
showCorrectness: PropTypes.bool,
|
|
25393
|
-
correct: PropTypes.bool,
|
|
25394
|
-
originalIndex: PropTypes.number,
|
|
25395
|
-
isNoneOfTheAbove: PropTypes.bool
|
|
25396
|
-
}));
|
|
25397
|
-
var radioBorderColor = radioBorderColor$2;
|
|
25286
|
+
} = Util; // exported for tests
|
|
25398
25287
|
|
|
25399
|
-
|
|
25400
|
-
|
|
25401
|
-
|
|
25402
|
-
|
|
25403
|
-
|
|
25404
|
-
// TODO(mdr): This keeps the ID stable across re-renders on the
|
|
25405
|
-
// same machine, but, at time of writing, the server's state
|
|
25406
|
-
// isn't rehydrated to the client during SSR, so the server and
|
|
25407
|
-
// client will generate different IDs and cause a mismatch
|
|
25408
|
-
// during SSR :(
|
|
25409
|
-
radioGroupName: _.uniqueId("perseus_radio_")
|
|
25410
|
-
});
|
|
25411
|
-
|
|
25412
|
-
_defineProperty(this, "updateChoice", (choiceIndex, newValues) => {
|
|
25413
|
-
// Get the baseline `checked` values. If we're checking a new answer
|
|
25414
|
-
// and multiple-select is not on, we should clear all choices to be
|
|
25415
|
-
// unchecked. Otherwise, we should copy the old checked values.
|
|
25416
|
-
var newCheckedList;
|
|
25417
|
-
|
|
25418
|
-
if (newValues.checked && !this.props.multipleSelect) {
|
|
25419
|
-
newCheckedList = this.props.choices.map(_ => false);
|
|
25420
|
-
} else {
|
|
25421
|
-
newCheckedList = this.props.choices.map(c => c.checked);
|
|
25422
|
-
} // Get the baseline `crossedOut` values.
|
|
25423
|
-
|
|
25424
|
-
|
|
25425
|
-
var newCrossedOutList = this.props.choices.map(c => c.crossedOut); // Update this choice's `checked` and `crossedOut` values.
|
|
25426
|
-
|
|
25427
|
-
newCheckedList[choiceIndex] = newValues.checked;
|
|
25428
|
-
newCrossedOutList[choiceIndex] = newValues.crossedOut;
|
|
25429
|
-
this.props.onChange({
|
|
25430
|
-
checked: newCheckedList,
|
|
25431
|
-
crossedOut: newCrossedOutList
|
|
25288
|
+
function getInstructionsText(multipleSelect, countChoices, numCorrect) {
|
|
25289
|
+
if (multipleSelect) {
|
|
25290
|
+
if (countChoices) {
|
|
25291
|
+
return i18n._("Choose %(numCorrect)s answers:", {
|
|
25292
|
+
numCorrect: numCorrect
|
|
25432
25293
|
});
|
|
25433
|
-
}
|
|
25434
|
-
|
|
25435
|
-
_defineProperty(this, "focus", i => {
|
|
25436
|
-
// $FlowFixMe[incompatible-use]
|
|
25437
|
-
// $FlowFixMe[prop-missing]
|
|
25438
|
-
ReactDOM__default.findDOMNode(this.refs["radio" + (i || 0)]).focus(); // eslint-disable-line react/no-string-refs
|
|
25439
|
-
|
|
25440
|
-
return true;
|
|
25441
|
-
});
|
|
25442
|
-
|
|
25443
|
-
_defineProperty(this, "getInstructionsText", () => {
|
|
25444
|
-
if (this.props.multipleSelect) {
|
|
25445
|
-
if (this.props.countChoices) {
|
|
25446
|
-
return i18n._("Choose %(numCorrect)s answers:", {
|
|
25447
|
-
numCorrect: this.props.numCorrect
|
|
25448
|
-
});
|
|
25449
|
-
}
|
|
25450
|
-
|
|
25451
|
-
return i18n._("Choose all answers that apply:");
|
|
25452
|
-
}
|
|
25453
|
-
|
|
25454
|
-
return i18n._("Choose 1 answer:");
|
|
25455
|
-
});
|
|
25456
|
-
|
|
25457
|
-
_defineProperty(this, "deselectEnabled", () => {
|
|
25458
|
-
// We want to force enable deselect on mobile.
|
|
25459
|
-
return this.props.apiOptions.isMobile || this.props.deselectEnabled;
|
|
25460
|
-
});
|
|
25461
|
-
|
|
25462
|
-
_defineProperty(this, "goToChoice", newChoiceIndex => {
|
|
25463
|
-
var numChoices = this.props.choices.length; // Wrap the newChoiceIndex around the start/end, if necessary.
|
|
25464
|
-
|
|
25465
|
-
if (newChoiceIndex < 0) {
|
|
25466
|
-
newChoiceIndex += numChoices;
|
|
25467
|
-
}
|
|
25468
|
-
|
|
25469
|
-
if (newChoiceIndex >= numChoices) {
|
|
25470
|
-
newChoiceIndex -= numChoices;
|
|
25471
|
-
} // Focus the new choice's input element.
|
|
25472
|
-
// eslint-disable-next-line react/no-string-refs
|
|
25473
|
-
|
|
25474
|
-
|
|
25475
|
-
var choiceInstance = this.refs["radio".concat(newChoiceIndex)];
|
|
25476
|
-
|
|
25477
|
-
if (!choiceInstance) {
|
|
25478
|
-
Log.error("found no choice at index ".concat(newChoiceIndex, ", even after ") + "wrapping it to be within bounds", Errors.Internal, {
|
|
25479
|
-
loggedMetadata: {
|
|
25480
|
-
choices: JSON.stringify(this.props.choices)
|
|
25481
|
-
}
|
|
25482
|
-
});
|
|
25483
|
-
return;
|
|
25484
|
-
}
|
|
25485
|
-
|
|
25486
|
-
choiceInstance.focusInput(); // We aren't going to change any choices' crossed-out state, but we
|
|
25487
|
-
// _are_ going to deselect all choices.
|
|
25488
|
-
|
|
25489
|
-
var newCrossedOutList = this.props.choices.map(c => c.crossedOut);
|
|
25490
|
-
var newCheckedList = this.props.choices.map(c => false); // And, if the new choice isn't crossed out, we'll select it.
|
|
25491
|
-
|
|
25492
|
-
if (!this.props.choices[newChoiceIndex].crossedOut) {
|
|
25493
|
-
newCheckedList[newChoiceIndex] = true;
|
|
25494
|
-
}
|
|
25294
|
+
}
|
|
25495
25295
|
|
|
25496
|
-
|
|
25497
|
-
checked: newCheckedList,
|
|
25498
|
-
crossedOut: newCrossedOutList
|
|
25499
|
-
});
|
|
25500
|
-
});
|
|
25296
|
+
return i18n._("Choose all answers that apply:");
|
|
25501
25297
|
}
|
|
25502
25298
|
|
|
25503
|
-
|
|
25504
|
-
|
|
25505
|
-
|
|
25506
|
-
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
|
|
25299
|
+
return i18n._("Choose 1 answer:");
|
|
25300
|
+
}
|
|
25301
|
+
|
|
25302
|
+
function BaseRadio(props) {
|
|
25303
|
+
var {
|
|
25304
|
+
apiOptions,
|
|
25305
|
+
reviewModeRubric,
|
|
25306
|
+
choices,
|
|
25307
|
+
editMode,
|
|
25308
|
+
multipleSelect,
|
|
25309
|
+
labelWrap,
|
|
25310
|
+
countChoices,
|
|
25311
|
+
numCorrect,
|
|
25312
|
+
isLastUsedWidget,
|
|
25313
|
+
registerFocusFunction
|
|
25314
|
+
} = props; // useEffect doesn't have previous props
|
|
25315
|
+
|
|
25316
|
+
var prevReviewModeRubric = useRef();
|
|
25317
|
+
var choiceRefs = useRef([]);
|
|
25318
|
+
useEffect(() => {
|
|
25319
|
+
// Switching into review mode can sometimes cause the selected answer
|
|
25510
25320
|
// to scroll out of view - for example, when we reveal all those tall
|
|
25511
25321
|
// rationales. This can be disorienting for the user.
|
|
25512
25322
|
//
|
|
@@ -25519,17 +25329,25 @@ class BaseRadio extends React.Component {
|
|
|
25519
25329
|
// the most recently used widget (because, if the user has since
|
|
25520
25330
|
// touched something else, they're probably not trying to keep their
|
|
25521
25331
|
// eye on this widget anymore).
|
|
25522
|
-
|
|
25523
|
-
if (apiOptions.canScrollPage && isLastUsedWidget && reviewModeRubric && !prevProps.reviewModeRubric) {
|
|
25332
|
+
if (apiOptions.canScrollPage && isLastUsedWidget && reviewModeRubric && !prevReviewModeRubric.current) {
|
|
25524
25333
|
var checkedIndex = choices.findIndex(c => c.checked);
|
|
25525
25334
|
|
|
25526
25335
|
if (checkedIndex >= 0) {
|
|
25527
|
-
var
|
|
25528
|
-
|
|
25529
|
-
|
|
25336
|
+
var ref = choiceRefs.current[checkedIndex]; // note(matthew): we know this is only getting passed
|
|
25337
|
+
// to a WB Clickable button, so we force it to be of
|
|
25338
|
+
// type HTMLButtonElement
|
|
25339
|
+
|
|
25340
|
+
var anyNode = ReactDOM__default.findDOMNode(ref.current);
|
|
25341
|
+
var buttonNode = anyNode;
|
|
25342
|
+
|
|
25343
|
+
if (buttonNode) {
|
|
25344
|
+
scrollElementIntoView(buttonNode);
|
|
25345
|
+
}
|
|
25530
25346
|
}
|
|
25531
25347
|
}
|
|
25532
|
-
|
|
25348
|
+
|
|
25349
|
+
prevReviewModeRubric.current = reviewModeRubric;
|
|
25350
|
+
}, [apiOptions, choices, isLastUsedWidget, reviewModeRubric]); // When a particular choice's `onChange` handler is called, indicating a
|
|
25533
25351
|
// change in a single choice's values, we need to call our `onChange`
|
|
25534
25352
|
// handler in order to notify our parent. However, our API with our parent
|
|
25535
25353
|
// is that we always provide *all* values for *all* choices, even if just
|
|
@@ -25538,181 +25356,197 @@ class BaseRadio extends React.Component {
|
|
|
25538
25356
|
// will usually cause the old answer to become unchecked.)
|
|
25539
25357
|
//
|
|
25540
25358
|
// So, given the new values for a particular choice, compute the new values
|
|
25541
|
-
// for all choices, and pass them to `
|
|
25359
|
+
// for all choices, and pass them to `onChange`.
|
|
25542
25360
|
//
|
|
25543
25361
|
// `newValues` is an object with two keys: `checked` and `crossedOut`. Each
|
|
25544
25362
|
// contains a boolean value specifying the new checked and crossed-out
|
|
25545
25363
|
// value of this choice.
|
|
25546
25364
|
|
|
25365
|
+
function updateChoice(choiceIndex, newValues) {
|
|
25366
|
+
var {
|
|
25367
|
+
multipleSelect,
|
|
25368
|
+
choices,
|
|
25369
|
+
onChange
|
|
25370
|
+
} = props; // Get the baseline `checked` values. If we're checking a new answer
|
|
25371
|
+
// and multiple-select is not on, we should clear all choices to be
|
|
25372
|
+
// unchecked. Otherwise, we should copy the old checked values.
|
|
25547
25373
|
|
|
25548
|
-
|
|
25549
|
-
var inputType = this.props.multipleSelect ? "checkbox" : "radio";
|
|
25550
|
-
var rubric = this.props.reviewModeRubric;
|
|
25551
|
-
var reviewMode = !!rubric;
|
|
25552
|
-
var styles = BaseRadio.styles;
|
|
25553
|
-
var sat = this.props.apiOptions.satStyling;
|
|
25554
|
-
var isMobile = this.props.apiOptions.isMobile;
|
|
25555
|
-
var choices = this.props.choices;
|
|
25556
|
-
var firstChoiceHighlighted = choices[0].highlighted;
|
|
25557
|
-
var lastChoiceHighlighted = choices[choices.length - 1].highlighted;
|
|
25558
|
-
var className = classNames("perseus-widget-radio", !this.props.editMode && "perseus-rendered-radio", css(styles.radio, // SAT doesn't use the "responsive styling" as it conflicts
|
|
25559
|
-
// with their custom theming.
|
|
25560
|
-
!sat && styles.responsiveRadioContainer, !sat && firstChoiceHighlighted && isMobile && styles.radioContainerFirstHighlighted, !sat && lastChoiceHighlighted && isMobile && styles.radioContainerLastHighlighted, sat && styles.satRadio));
|
|
25561
|
-
var instructionsClassName = classNames("instructions", css(styles.instructions, isMobile && styles.instructionsMobile));
|
|
25562
|
-
var instructions = this.getInstructionsText();
|
|
25563
|
-
var shouldShowInstructions = !sat;
|
|
25564
|
-
var responsiveClassName = css(styles.responsiveFieldset);
|
|
25565
|
-
var fieldset = /*#__PURE__*/React.createElement("fieldset", {
|
|
25566
|
-
className: "perseus-widget-radio-fieldset ".concat(responsiveClassName)
|
|
25567
|
-
}, /*#__PURE__*/React.createElement("legend", {
|
|
25568
|
-
className: "perseus-sr-only"
|
|
25569
|
-
}, instructions), shouldShowInstructions && /*#__PURE__*/React.createElement("div", {
|
|
25570
|
-
className: instructionsClassName
|
|
25571
|
-
}, instructions), /*#__PURE__*/React.createElement("ul", {
|
|
25572
|
-
className: className,
|
|
25573
|
-
style: {
|
|
25574
|
-
listStyle: "none"
|
|
25575
|
-
}
|
|
25576
|
-
}, this.props.choices.map(function (choice, i) {
|
|
25577
|
-
var Element = Choice;
|
|
25578
|
-
var elementProps = {
|
|
25579
|
-
ref: "radio".concat(i),
|
|
25580
|
-
apiOptions: this.props.apiOptions,
|
|
25581
|
-
checked: choice.checked,
|
|
25582
|
-
crossedOut: choice.crossedOut,
|
|
25583
|
-
previouslyAnswered: choice.previouslyAnswered,
|
|
25584
|
-
reviewMode,
|
|
25585
|
-
correct: choice.correct,
|
|
25586
|
-
rationale: choice.rationale,
|
|
25587
|
-
content: choice.content,
|
|
25588
|
-
disabled: this.props.apiOptions.readOnly || choice.disabled,
|
|
25589
|
-
editMode: this.props.editMode,
|
|
25590
|
-
groupName: this.state.radioGroupName,
|
|
25591
|
-
isLastChoice: i === this.props.choices.length - 1,
|
|
25592
|
-
showCorrectness: reviewMode || !!choice.showCorrectness,
|
|
25593
|
-
showRationale: choice.hasRationale && (reviewMode || choice.showRationale),
|
|
25594
|
-
type: inputType,
|
|
25595
|
-
pos: i,
|
|
25596
|
-
deselectEnabled: this.deselectEnabled(),
|
|
25597
|
-
onChange: newValues => {
|
|
25598
|
-
this.updateChoice(i, newValues);
|
|
25599
|
-
},
|
|
25600
|
-
goToPrevChoice: () => this.goToChoice(i - 1),
|
|
25601
|
-
goToNextChoice: () => this.goToChoice(i + 1)
|
|
25602
|
-
};
|
|
25374
|
+
var newCheckedList;
|
|
25603
25375
|
|
|
25604
|
-
|
|
25605
|
-
|
|
25376
|
+
if (newValues.checked && !multipleSelect) {
|
|
25377
|
+
newCheckedList = choices.map(_ => false);
|
|
25378
|
+
} else {
|
|
25379
|
+
newCheckedList = choices.map(c => c.checked);
|
|
25380
|
+
} // Get the baseline `crossedOut` values.
|
|
25606
25381
|
|
|
25607
|
-
_.extend(elementProps, {
|
|
25608
|
-
showContent: choice.revealNoneOfTheAbove
|
|
25609
|
-
});
|
|
25610
|
-
}
|
|
25611
25382
|
|
|
25612
|
-
|
|
25613
|
-
|
|
25614
|
-
|
|
25615
|
-
|
|
25616
|
-
|
|
25617
|
-
|
|
25618
|
-
|
|
25619
|
-
|
|
25620
|
-
|
|
25621
|
-
|
|
25622
|
-
|
|
25623
|
-
|
|
25624
|
-
|
|
25625
|
-
|
|
25626
|
-
|
|
25627
|
-
|
|
25628
|
-
|
|
25629
|
-
|
|
25630
|
-
|
|
25631
|
-
|
|
25632
|
-
|
|
25633
|
-
|
|
25634
|
-
|
|
25635
|
-
|
|
25636
|
-
|
|
25637
|
-
|
|
25638
|
-
|
|
25639
|
-
|
|
25640
|
-
|
|
25641
|
-
|
|
25642
|
-
|
|
25643
|
-
|
|
25644
|
-
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25651
|
-
|
|
25652
|
-
|
|
25653
|
-
|
|
25383
|
+
var newCrossedOutList = choices.map(c => c.crossedOut); // Update this choice's `checked` and `crossedOut` values.
|
|
25384
|
+
|
|
25385
|
+
newCheckedList[choiceIndex] = newValues.checked;
|
|
25386
|
+
newCrossedOutList[choiceIndex] = newValues.crossedOut;
|
|
25387
|
+
onChange({
|
|
25388
|
+
checked: newCheckedList,
|
|
25389
|
+
crossedOut: newCrossedOutList
|
|
25390
|
+
});
|
|
25391
|
+
} // register a callback with the parent that allows
|
|
25392
|
+
// the parent to focus an individual choice
|
|
25393
|
+
|
|
25394
|
+
|
|
25395
|
+
registerFocusFunction === null || registerFocusFunction === void 0 ? void 0 : registerFocusFunction(choiceIndex => {
|
|
25396
|
+
var ref = choiceRefs.current[choiceIndex || 0]; // note(matthew): we know this is only getting passed
|
|
25397
|
+
// to a WB Clickable button, so we force it to be of
|
|
25398
|
+
// type HTMLButtonElement
|
|
25399
|
+
|
|
25400
|
+
var anyNode = ReactDOM__default.findDOMNode(ref.current);
|
|
25401
|
+
var buttonNode = anyNode;
|
|
25402
|
+
|
|
25403
|
+
if (buttonNode) {
|
|
25404
|
+
buttonNode.focus();
|
|
25405
|
+
} else {
|
|
25406
|
+
return false;
|
|
25407
|
+
}
|
|
25408
|
+
|
|
25409
|
+
return true;
|
|
25410
|
+
}); // some commonly used shorthands
|
|
25411
|
+
|
|
25412
|
+
var reviewMode = !!reviewModeRubric;
|
|
25413
|
+
var sat = apiOptions.satStyling;
|
|
25414
|
+
var isMobile = apiOptions.isMobile;
|
|
25415
|
+
var firstChoiceHighlighted = choices[0].highlighted;
|
|
25416
|
+
var lastChoiceHighlighted = choices[choices.length - 1].highlighted;
|
|
25417
|
+
var className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$r.radio, // SAT doesn't use the "responsive styling" as it conflicts
|
|
25418
|
+
// with their custom theming.
|
|
25419
|
+
!sat && styles$r.responsiveRadioContainer, !sat && firstChoiceHighlighted && isMobile && styles$r.radioContainerFirstHighlighted, !sat && lastChoiceHighlighted && isMobile && styles$r.radioContainerLastHighlighted, sat && styles$r.satRadio));
|
|
25420
|
+
var instructionsClassName = classNames("instructions", css(styles$r.instructions, isMobile && styles$r.instructionsMobile));
|
|
25421
|
+
var instructions = getInstructionsText(multipleSelect, countChoices, numCorrect);
|
|
25422
|
+
var shouldShowInstructions = !sat;
|
|
25423
|
+
var responsiveClassName = css(styles$r.responsiveFieldset);
|
|
25424
|
+
var fieldset = /*#__PURE__*/React.createElement("fieldset", {
|
|
25425
|
+
className: "perseus-widget-radio-fieldset ".concat(responsiveClassName)
|
|
25426
|
+
}, /*#__PURE__*/React.createElement("legend", {
|
|
25427
|
+
className: "perseus-sr-only"
|
|
25428
|
+
}, instructions), shouldShowInstructions && /*#__PURE__*/React.createElement("div", {
|
|
25429
|
+
className: instructionsClassName,
|
|
25430
|
+
"aria-hidden": "true"
|
|
25431
|
+
}, instructions), /*#__PURE__*/React.createElement("ul", {
|
|
25432
|
+
className: className,
|
|
25433
|
+
style: {
|
|
25434
|
+
listStyle: "none"
|
|
25435
|
+
}
|
|
25436
|
+
}, choices.map(function (choice, i) {
|
|
25437
|
+
var Element = Choice$1;
|
|
25438
|
+
var ref = /*#__PURE__*/React.createRef();
|
|
25439
|
+
choiceRefs.current[i] = ref;
|
|
25440
|
+
var elementProps = {
|
|
25441
|
+
apiOptions: apiOptions,
|
|
25442
|
+
multipleSelect: multipleSelect,
|
|
25443
|
+
checked: choice.checked,
|
|
25444
|
+
crossedOut: choice.crossedOut,
|
|
25445
|
+
previouslyAnswered: choice.previouslyAnswered,
|
|
25446
|
+
reviewMode,
|
|
25447
|
+
correct: choice.correct,
|
|
25448
|
+
rationale: choice.rationale,
|
|
25449
|
+
content: choice.content,
|
|
25450
|
+
disabled: apiOptions.readOnly || choice.disabled,
|
|
25451
|
+
showCorrectness: reviewMode || !!choice.showCorrectness,
|
|
25452
|
+
showRationale: choice.hasRationale && (reviewMode || choice.showRationale),
|
|
25453
|
+
pos: i,
|
|
25454
|
+
onChange: newValues => {
|
|
25455
|
+
updateChoice(i, newValues);
|
|
25456
|
+
},
|
|
25457
|
+
ref
|
|
25458
|
+
};
|
|
25459
|
+
|
|
25460
|
+
if (choice.isNoneOfTheAbove) {
|
|
25461
|
+
Element = ChoiceNoneAbove$1;
|
|
25462
|
+
|
|
25463
|
+
_.extend(elementProps, {
|
|
25464
|
+
showContent: choice.revealNoneOfTheAbove
|
|
25465
|
+
});
|
|
25466
|
+
}
|
|
25467
|
+
|
|
25468
|
+
var nextChoice = choices[i + 1];
|
|
25469
|
+
var nextChoiceHighlighted = !!nextChoice && nextChoice.highlighted;
|
|
25470
|
+
|
|
25471
|
+
var aphroditeClassName = checked => {
|
|
25472
|
+
// Whether or not to show correctness borders
|
|
25473
|
+
// for this choice and the next choice.
|
|
25474
|
+
var satShowCorrectness = sat && reviewMode && checked;
|
|
25475
|
+
var satShowCorrectnessNext = sat && reviewMode && nextChoice && nextChoice.checked;
|
|
25476
|
+
return 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);
|
|
25477
|
+
}; // HACK(abdulrahman): Preloads the selection-state
|
|
25478
|
+
// css because of a bug that causes iOS to lag
|
|
25479
|
+
// when selecting the button for the first time.
|
|
25480
|
+
|
|
25654
25481
|
|
|
25655
|
-
|
|
25482
|
+
aphroditeClassName(true);
|
|
25483
|
+
var correctnessClass; // reviewMode is only true if there's a rubric
|
|
25484
|
+
// but Flow doesn't understand that
|
|
25485
|
+
|
|
25486
|
+
if (reviewMode && reviewModeRubric) {
|
|
25487
|
+
correctnessClass = reviewModeRubric.choices[i].correct ? ClassNames.CORRECT : ClassNames.INCORRECT;
|
|
25488
|
+
}
|
|
25489
|
+
|
|
25490
|
+
var className = classNames(aphroditeClassName(choice.checked), // TODO(aria): Make test case for these API
|
|
25491
|
+
// classNames
|
|
25492
|
+
ClassNames.RADIO.OPTION, choice.checked && ClassNames.RADIO.SELECTED, correctnessClass); // In edit mode, the Choice renders a Div in order to
|
|
25493
|
+
// allow for the contentEditable area to be selected
|
|
25494
|
+
// (label forces any clicks inside to select the input
|
|
25495
|
+
// element) We have to add some extra behavior to make
|
|
25496
|
+
// sure that we can still check the choice.
|
|
25497
|
+
|
|
25498
|
+
var listElem = null;
|
|
25499
|
+
var clickHandler = null;
|
|
25500
|
+
|
|
25501
|
+
if (editMode) {
|
|
25502
|
+
clickHandler = e => {
|
|
25503
|
+
// Traverse the parent nodes of the clicked
|
|
25504
|
+
// element.
|
|
25505
|
+
var elem = e.target;
|
|
25506
|
+
|
|
25507
|
+
while (elem && elem !== listElem) {
|
|
25508
|
+
// If the clicked element is inside of the
|
|
25509
|
+
// radio icon, then we want to trigger the
|
|
25510
|
+
// check by flipping the choice of the icon.
|
|
25511
|
+
if (elem.getAttribute("data-is-radio-icon")) {
|
|
25512
|
+
this.updateChoice(i, {
|
|
25513
|
+
checked: !choice.checked,
|
|
25514
|
+
crossedOut: choice.crossedOut
|
|
25515
|
+
});
|
|
25516
|
+
return;
|
|
25656
25517
|
}
|
|
25657
|
-
};
|
|
25658
|
-
} // TODO(mattdr): Index isn't a *good* choice of key
|
|
25659
|
-
// here; is there a better one? Can we use choice
|
|
25660
|
-
// content somehow? Would changing our choice of key
|
|
25661
|
-
// somehow break something happening inside a choice's
|
|
25662
|
-
// child Renderers, by changing when we mount/unmount?
|
|
25663
25518
|
|
|
25519
|
+
elem = elem.parentNode;
|
|
25520
|
+
}
|
|
25521
|
+
};
|
|
25522
|
+
} // TODO(mattdr): Index isn't a *good* choice of key
|
|
25523
|
+
// here; is there a better one? Can we use choice
|
|
25524
|
+
// content somehow? Would changing our choice of key
|
|
25525
|
+
// somehow break something happening inside a choice's
|
|
25526
|
+
// child Renderers, by changing when we mount/unmount?
|
|
25664
25527
|
|
|
25665
|
-
return /*#__PURE__*/React.createElement("li", {
|
|
25666
|
-
key: i,
|
|
25667
|
-
ref: e => listElem = e,
|
|
25668
|
-
className: className,
|
|
25669
|
-
onClick: clickHandler,
|
|
25670
|
-
onTouchStart: !this.props.labelWrap ? null : captureScratchpadTouchStart$2
|
|
25671
|
-
}, /*#__PURE__*/React.createElement(Element, elementProps));
|
|
25672
|
-
}, this))); // Allow for horizontal scrolling if content is too wide, which may be
|
|
25673
|
-
// an issue especially on phones.
|
|
25674
|
-
// This is disabled in SAT, since it conflicts with their theming.
|
|
25675
25528
|
|
|
25676
|
-
return /*#__PURE__*/React.createElement("
|
|
25677
|
-
|
|
25678
|
-
|
|
25679
|
-
|
|
25529
|
+
return /*#__PURE__*/React.createElement("li", {
|
|
25530
|
+
key: i,
|
|
25531
|
+
ref: e => listElem = e,
|
|
25532
|
+
className: className,
|
|
25533
|
+
onClick: clickHandler,
|
|
25534
|
+
onTouchStart: !labelWrap ? null : captureScratchpadTouchStart$2
|
|
25535
|
+
}, /*#__PURE__*/React.createElement(Element, elementProps));
|
|
25536
|
+
}, this))); // Allow for horizontal scrolling if content is too wide, which may be
|
|
25537
|
+
// an issue especially on phones.
|
|
25538
|
+
// This is disabled in SAT, since it conflicts with their theming.
|
|
25680
25539
|
|
|
25540
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
25541
|
+
className: css(!sat && styles$r.responsiveContainer)
|
|
25542
|
+
}, fieldset);
|
|
25681
25543
|
}
|
|
25682
25544
|
|
|
25683
|
-
|
|
25684
|
-
|
|
25685
|
-
|
|
25686
|
-
|
|
25687
|
-
|
|
25688
|
-
styling: PropTypes.shape({
|
|
25689
|
-
radioStyleVersion: PropTypes.oneOf(["intermediate", "final"])
|
|
25690
|
-
}),
|
|
25691
|
-
canScrollPage: PropTypes.bool
|
|
25692
|
-
}),
|
|
25693
|
-
choices: ChoicesType,
|
|
25694
|
-
deselectEnabled: PropTypes.bool,
|
|
25695
|
-
editMode: PropTypes.bool,
|
|
25696
|
-
labelWrap: PropTypes.bool,
|
|
25697
|
-
countChoices: PropTypes.bool,
|
|
25698
|
-
numCorrect: PropTypes.number,
|
|
25699
|
-
multipleSelect: PropTypes.bool,
|
|
25700
|
-
reviewModeRubric: PropTypes.shape({
|
|
25701
|
-
choices: ChoicesType
|
|
25702
|
-
}),
|
|
25703
|
-
// A callback indicating that this choice has changed. Its argument is
|
|
25704
|
-
// an object with two keys: `checked` and `crossedOut`. Each contains
|
|
25705
|
-
// an array of boolean values, specifying the new checked and
|
|
25706
|
-
// crossed-out value of each choice.
|
|
25707
|
-
onChange: PropTypes.func,
|
|
25708
|
-
// Whether this widget was the most recently used widget in this
|
|
25709
|
-
// Renderer. Determines whether we'll auto-scroll the page upon
|
|
25710
|
-
// entering review mode.
|
|
25711
|
-
isLastUsedWidget: PropTypes.bool
|
|
25712
|
-
});
|
|
25713
|
-
|
|
25714
|
-
_defineProperty(BaseRadio, "styles", StyleSheet.create({
|
|
25715
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25545
|
+
BaseRadio.defaultProps = {
|
|
25546
|
+
editMode: false,
|
|
25547
|
+
multipleSelect: false
|
|
25548
|
+
};
|
|
25549
|
+
var styles$r = StyleSheet.create({
|
|
25716
25550
|
instructions: {
|
|
25717
25551
|
display: "block",
|
|
25718
25552
|
color: gray17,
|
|
@@ -25722,7 +25556,6 @@ _defineProperty(BaseRadio, "styles", StyleSheet.create({
|
|
|
25722
25556
|
fontWeight: "bold",
|
|
25723
25557
|
marginBottom: 16
|
|
25724
25558
|
},
|
|
25725
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25726
25559
|
instructionsMobile: {
|
|
25727
25560
|
fontSize: 18,
|
|
25728
25561
|
[mediaQueries.smOrSmaller]: {
|
|
@@ -25736,36 +25569,30 @@ _defineProperty(BaseRadio, "styles", StyleSheet.create({
|
|
|
25736
25569
|
fontSize: 20
|
|
25737
25570
|
}
|
|
25738
25571
|
},
|
|
25739
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25740
25572
|
radio: {
|
|
25741
25573
|
// Avoid centering
|
|
25742
25574
|
width: "100%"
|
|
25743
25575
|
},
|
|
25744
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25745
25576
|
responsiveRadioContainer: {
|
|
25746
|
-
borderBottom: "1px solid ".concat(radioBorderColor),
|
|
25747
|
-
borderTop: "1px solid ".concat(radioBorderColor),
|
|
25577
|
+
borderBottom: "1px solid ".concat(radioBorderColor$1),
|
|
25578
|
+
borderTop: "1px solid ".concat(radioBorderColor$1),
|
|
25748
25579
|
width: "auto",
|
|
25749
25580
|
[mediaQueries.smOrSmaller]: {
|
|
25750
25581
|
marginLeft: negativePhoneMargin,
|
|
25751
25582
|
marginRight: negativePhoneMargin
|
|
25752
25583
|
}
|
|
25753
25584
|
},
|
|
25754
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25755
25585
|
radioContainerFirstHighlighted: {
|
|
25756
25586
|
borderTop: "1px solid rgba(0, 0, 0, 0)"
|
|
25757
25587
|
},
|
|
25758
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25759
25588
|
radioContainerLastHighlighted: {
|
|
25760
25589
|
borderBottom: "1px solid rgba(0, 0, 0, 0)"
|
|
25761
25590
|
},
|
|
25762
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25763
25591
|
satRadio: {
|
|
25764
25592
|
background: "none",
|
|
25765
25593
|
marginLeft: 0,
|
|
25766
25594
|
userSelect: "none"
|
|
25767
25595
|
},
|
|
25768
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25769
25596
|
satRadioOption: {
|
|
25770
25597
|
margin: 0,
|
|
25771
25598
|
padding: 0,
|
|
@@ -25774,57 +25601,40 @@ _defineProperty(BaseRadio, "styles", StyleSheet.create({
|
|
|
25774
25601
|
borderTop: "1px solid #ccc"
|
|
25775
25602
|
}
|
|
25776
25603
|
},
|
|
25777
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25778
25604
|
satRadioOptionCorrect: {
|
|
25779
25605
|
borderBottomColor: satCorrectBorderColor,
|
|
25780
25606
|
":first-child": {
|
|
25781
25607
|
borderTopColor: satCorrectBorderColor
|
|
25782
25608
|
}
|
|
25783
25609
|
},
|
|
25784
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25785
25610
|
satRadioOptionIncorrect: {
|
|
25786
25611
|
borderBottomColor: satIncorrectBorderColor,
|
|
25787
25612
|
":first-child": {
|
|
25788
25613
|
borderTopColor: satIncorrectBorderColor
|
|
25789
25614
|
}
|
|
25790
25615
|
},
|
|
25791
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25792
25616
|
satRadioOptionNextCorrect: {
|
|
25793
25617
|
borderBottomColor: satCorrectBorderColor
|
|
25794
25618
|
},
|
|
25795
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25796
25619
|
satRadioOptionNextIncorrect: {
|
|
25797
25620
|
borderBottomColor: satIncorrectBorderColor
|
|
25798
25621
|
},
|
|
25799
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25800
25622
|
satReviewRadioOption: {
|
|
25801
25623
|
pointerEvents: "none"
|
|
25802
25624
|
},
|
|
25803
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25804
25625
|
item: {
|
|
25805
25626
|
marginLeft: 20
|
|
25806
25627
|
},
|
|
25807
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25808
|
-
inlineItem: {
|
|
25809
|
-
display: "inline-block",
|
|
25810
|
-
paddingLeft: 20,
|
|
25811
|
-
verticalAlign: "middle" // See http://stackoverflow.com/q/8120466 for explanation of
|
|
25812
|
-
// why vertical align property is needed
|
|
25813
|
-
|
|
25814
|
-
},
|
|
25815
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25816
25628
|
responsiveItem: {
|
|
25817
25629
|
marginLeft: 0,
|
|
25818
25630
|
padding: 0,
|
|
25819
25631
|
":not(:last-child)": {
|
|
25820
|
-
borderBottom: "1px solid ".concat(radioBorderColor)
|
|
25632
|
+
borderBottom: "1px solid ".concat(radioBorderColor$1)
|
|
25821
25633
|
}
|
|
25822
25634
|
},
|
|
25823
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25824
25635
|
selectedItem: {
|
|
25825
25636
|
background: "white"
|
|
25826
25637
|
},
|
|
25827
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25828
25638
|
aboveBackdrop: {
|
|
25829
25639
|
position: "relative",
|
|
25830
25640
|
// HACK(emily): We want selected choices to show up above our
|
|
@@ -25833,20 +25643,17 @@ _defineProperty(BaseRadio, "styles", StyleSheet.create({
|
|
|
25833
25643
|
// coordinated between here and webapp. :(
|
|
25834
25644
|
zIndex: 1062
|
|
25835
25645
|
},
|
|
25836
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25837
25646
|
aboveBackdropMobile: {
|
|
25838
25647
|
boxShadow: "0 0 4px 0 rgba(0, 0, 0, 0.2)," + "0 0 2px 0 rgba(0, 0, 0, 0.1)",
|
|
25839
25648
|
":not(:last-child)": {
|
|
25840
25649
|
borderBottom: "1px solid rgba(0, 0, 0, 0)"
|
|
25841
25650
|
}
|
|
25842
25651
|
},
|
|
25843
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25844
25652
|
nextHighlighted: {
|
|
25845
25653
|
":not(:last-child)": {
|
|
25846
25654
|
borderBottom: "1px solid rgba(0, 0, 0, 0)"
|
|
25847
25655
|
}
|
|
25848
25656
|
},
|
|
25849
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25850
25657
|
responsiveContainer: {
|
|
25851
25658
|
overflow: "auto",
|
|
25852
25659
|
marginLeft: negativePhoneMargin,
|
|
@@ -25854,22 +25661,22 @@ _defineProperty(BaseRadio, "styles", StyleSheet.create({
|
|
|
25854
25661
|
paddingLeft: phoneMargin // paddingRight is handled by responsiveFieldset
|
|
25855
25662
|
|
|
25856
25663
|
},
|
|
25857
|
-
// eslint-disable-next-line react-native/no-unused-styles
|
|
25858
25664
|
responsiveFieldset: {
|
|
25859
25665
|
paddingRight: phoneMargin
|
|
25860
25666
|
}
|
|
25861
|
-
}));
|
|
25862
|
-
|
|
25863
|
-
_defineProperty(BaseRadio, "defaultProps", {
|
|
25864
|
-
editMode: false
|
|
25865
25667
|
});
|
|
25866
25668
|
|
|
25867
25669
|
class Radio$1 extends React.Component {
|
|
25868
25670
|
constructor() {
|
|
25671
|
+
var _this;
|
|
25672
|
+
|
|
25869
25673
|
super(...arguments);
|
|
25674
|
+
_this = this;
|
|
25675
|
+
|
|
25676
|
+
_defineProperty(this, "focusFunction", void 0);
|
|
25870
25677
|
|
|
25871
|
-
_defineProperty(this, "_renderRenderer",
|
|
25872
|
-
content =
|
|
25678
|
+
_defineProperty(this, "_renderRenderer", function () {
|
|
25679
|
+
var content = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
|
|
25873
25680
|
var nextPassageRefId = 1;
|
|
25874
25681
|
var widgets = {};
|
|
25875
25682
|
var modContent = content.replace(/\{\{passage-ref (\d+) (\d+)(?: "([^"]*)")?\}\}/g, (match, passageNum, refNum, summaryText) => {
|
|
@@ -25901,19 +25708,14 @@ class Radio$1 extends React.Component {
|
|
|
25901
25708
|
key: "choiceContentRenderer",
|
|
25902
25709
|
content: modContent,
|
|
25903
25710
|
widgets: widgets,
|
|
25904
|
-
findExternalWidgets:
|
|
25711
|
+
findExternalWidgets: _this.props.findWidgets,
|
|
25905
25712
|
alwaysUpdate: true,
|
|
25906
|
-
linterContext: _objectSpread2(_objectSpread2({},
|
|
25713
|
+
linterContext: _objectSpread2(_objectSpread2({}, _this.props.linterContext), {}, {
|
|
25907
25714
|
blockHighlight: true
|
|
25908
25715
|
})
|
|
25909
25716
|
});
|
|
25910
25717
|
});
|
|
25911
25718
|
|
|
25912
|
-
_defineProperty(this, "focus", i => {
|
|
25913
|
-
// eslint-disable-next-line react/no-string-refs
|
|
25914
|
-
return this.refs.baseRadio.focus(i);
|
|
25915
|
-
});
|
|
25916
|
-
|
|
25917
25719
|
_defineProperty(this, "updateChoices", newValueLists => {
|
|
25918
25720
|
var {
|
|
25919
25721
|
choiceStates,
|
|
@@ -25924,24 +25726,17 @@ class Radio$1 extends React.Component {
|
|
|
25924
25726
|
// should clone the old `choiceStates` objects, in preparation to
|
|
25925
25727
|
// mutate them.
|
|
25926
25728
|
|
|
25927
|
-
var newChoiceStates
|
|
25928
|
-
|
|
25929
|
-
|
|
25930
|
-
|
|
25931
|
-
|
|
25932
|
-
|
|
25933
|
-
|
|
25934
|
-
|
|
25935
|
-
|
|
25936
|
-
rationaleShown: false,
|
|
25937
|
-
correctnessShown: false,
|
|
25938
|
-
previouslyAnswered: false,
|
|
25939
|
-
readOnly: false
|
|
25940
|
-
}));
|
|
25941
|
-
} // Mutate the new `choiceState` objects, according to the new `checked`
|
|
25729
|
+
var newChoiceStates = choiceStates ? choiceStates.map(state => _objectSpread2({}, state)) : choices.map(() => ({
|
|
25730
|
+
selected: false,
|
|
25731
|
+
crossedOut: false,
|
|
25732
|
+
highlighted: false,
|
|
25733
|
+
rationaleShown: false,
|
|
25734
|
+
correctnessShown: false,
|
|
25735
|
+
previouslyAnswered: false,
|
|
25736
|
+
readOnly: false
|
|
25737
|
+
})); // Mutate the new `choiceState` objects, according to the new `checked`
|
|
25942
25738
|
// and `crossedOut` values provided in `newValueLists`.
|
|
25943
25739
|
|
|
25944
|
-
|
|
25945
25740
|
newChoiceStates.forEach((choiceState, i) => {
|
|
25946
25741
|
choiceState.selected = newValueLists.checked[i];
|
|
25947
25742
|
choiceState.crossedOut = newValueLists.crossedOut[i];
|
|
@@ -26005,14 +25800,47 @@ class Radio$1 extends React.Component {
|
|
|
26005
25800
|
});
|
|
26006
25801
|
}
|
|
26007
25802
|
|
|
25803
|
+
// TODO(LP-10672): I think this might be unused right now. I can't find anywhere
|
|
25804
|
+
// that we pass a value to `.focus()` and it seems to have been used for
|
|
25805
|
+
// adding hints when editing.
|
|
25806
|
+
// See: https://github.com/Khan/perseus/blame/e18582b4b69959270b90e237ef1813899711ddfa/src/widgets/radio.js#L169
|
|
25807
|
+
focus(choiceIndex) {
|
|
25808
|
+
if (this.focusFunction) {
|
|
25809
|
+
return this.focusFunction(choiceIndex);
|
|
25810
|
+
}
|
|
25811
|
+
|
|
25812
|
+
return false;
|
|
25813
|
+
} // lets BaseRadio regiser a focus callback so widget
|
|
25814
|
+
// can focus an individual choice
|
|
25815
|
+
|
|
25816
|
+
|
|
25817
|
+
registerFocusFunction(fun) {
|
|
25818
|
+
this.focusFunction = fun;
|
|
25819
|
+
} // When `BaseRadio`'s `onChange` handler is called, indicating a change in
|
|
25820
|
+
// our choices' state, we need to call our `onChange` handler in order to
|
|
25821
|
+
// persist those changes in the item's Perseus state.
|
|
25822
|
+
//
|
|
25823
|
+
// So, given the new values for each choice, construct the new
|
|
25824
|
+
// `choiceStates` objects, and pass them to `this.props.onChange`.
|
|
25825
|
+
//
|
|
25826
|
+
// `newValueLists` is an object with two keys: `checked` and `crossedOut`.
|
|
25827
|
+
// Each contains an array of boolean values, specifying the new checked and
|
|
25828
|
+
// crossed-out value of each choice.
|
|
25829
|
+
//
|
|
25830
|
+
// NOTE(mdr): This method expects to be auto-bound. If this component is
|
|
25831
|
+
// converted to an ES6 class, take care to auto-bind this method!
|
|
25832
|
+
|
|
25833
|
+
|
|
26008
25834
|
render() {
|
|
26009
|
-
var
|
|
25835
|
+
var {
|
|
25836
|
+
choices
|
|
25837
|
+
} = this.props;
|
|
26010
25838
|
var choiceStates;
|
|
26011
25839
|
|
|
26012
25840
|
if (this.props.static) {
|
|
26013
|
-
choiceStates =
|
|
26014
|
-
selected:
|
|
26015
|
-
crossedOut:
|
|
25841
|
+
choiceStates = choices.map(choice => ({
|
|
25842
|
+
selected: !!choice.correct,
|
|
25843
|
+
crossedOut: false,
|
|
26016
25844
|
readOnly: true,
|
|
26017
25845
|
highlighted: false,
|
|
26018
25846
|
rationaleShown: true,
|
|
@@ -26020,12 +25848,12 @@ class Radio$1 extends React.Component {
|
|
|
26020
25848
|
previouslyAnswered: false
|
|
26021
25849
|
}));
|
|
26022
25850
|
} else if (this.props.choiceStates) {
|
|
26023
|
-
choiceStates = this.props.choiceStates;
|
|
25851
|
+
choiceStates = this.props.choiceStates;
|
|
26024
25852
|
} else if (this.props.values) {
|
|
26025
25853
|
// Support legacy choiceStates implementation
|
|
26026
25854
|
|
|
26027
25855
|
/* istanbul ignore next - props.values is deprecated */
|
|
26028
|
-
choiceStates =
|
|
25856
|
+
choiceStates = this.props.values.map(val => ({
|
|
26029
25857
|
selected: val,
|
|
26030
25858
|
crossedOut: false,
|
|
26031
25859
|
readOnly: false,
|
|
@@ -26035,7 +25863,7 @@ class Radio$1 extends React.Component {
|
|
|
26035
25863
|
previouslyAnswered: false
|
|
26036
25864
|
}));
|
|
26037
25865
|
} else {
|
|
26038
|
-
choiceStates =
|
|
25866
|
+
choiceStates = choices.map(() => ({
|
|
26039
25867
|
selected: false,
|
|
26040
25868
|
crossedOut: false,
|
|
26041
25869
|
readOnly: false,
|
|
@@ -26046,7 +25874,7 @@ class Radio$1 extends React.Component {
|
|
|
26046
25874
|
}));
|
|
26047
25875
|
}
|
|
26048
25876
|
|
|
26049
|
-
|
|
25877
|
+
var choicesProp = choices.map((choice, i) => {
|
|
26050
25878
|
var content = choice.isNoneOfTheAbove && !choice.content ? // we use i18n._ instead of $_ here because the content
|
|
26051
25879
|
// sent to a renderer needs to be a string, not a react
|
|
26052
25880
|
// node (/renderable/fragment).
|
|
@@ -26074,37 +25902,36 @@ class Radio$1 extends React.Component {
|
|
|
26074
25902
|
// TODO(emily): Come up with a more comprehensive way to solve
|
|
26075
25903
|
// this sort of "serialized state breaks when internal
|
|
26076
25904
|
// structure changes" problem.
|
|
26077
|
-
correct:
|
|
25905
|
+
correct: choice.correct === undefined ? !!reviewChoice && !!reviewChoice.correct : choice.correct,
|
|
26078
25906
|
disabled: readOnly,
|
|
26079
25907
|
hasRationale: !!choice.clue,
|
|
26080
25908
|
rationale: this._renderRenderer(choice.clue),
|
|
26081
25909
|
showRationale: rationaleShown,
|
|
26082
25910
|
showCorrectness: correctnessShown,
|
|
26083
|
-
isNoneOfTheAbove: choice.isNoneOfTheAbove,
|
|
26084
|
-
revealNoneOfTheAbove: this.props.questionCompleted && selected,
|
|
25911
|
+
isNoneOfTheAbove: !!choice.isNoneOfTheAbove,
|
|
25912
|
+
revealNoneOfTheAbove: !!(this.props.questionCompleted && selected),
|
|
26085
25913
|
crossedOut,
|
|
26086
25914
|
highlighted,
|
|
26087
25915
|
previouslyAnswered: previouslyAnswered
|
|
26088
25916
|
};
|
|
26089
25917
|
});
|
|
26090
|
-
return /*#__PURE__*/React.createElement(BaseRadio
|
|
26091
|
-
, {
|
|
26092
|
-
ref: "baseRadio",
|
|
25918
|
+
return /*#__PURE__*/React.createElement(BaseRadio, {
|
|
26093
25919
|
labelWrap: true,
|
|
26094
25920
|
multipleSelect: this.props.multipleSelect,
|
|
26095
25921
|
countChoices: this.props.countChoices,
|
|
26096
25922
|
numCorrect: this.props.numCorrect,
|
|
26097
|
-
choices:
|
|
25923
|
+
choices: choicesProp,
|
|
26098
25924
|
onChange: this.updateChoices,
|
|
26099
25925
|
reviewModeRubric: this.props.reviewModeRubric,
|
|
26100
25926
|
deselectEnabled: this.props.deselectEnabled,
|
|
26101
25927
|
apiOptions: this.props.apiOptions,
|
|
26102
|
-
isLastUsedWidget: this.props.isLastUsedWidget
|
|
25928
|
+
isLastUsedWidget: this.props.isLastUsedWidget,
|
|
25929
|
+
registerFocusFunction: i => this.registerFocusFunction(i)
|
|
26103
25930
|
});
|
|
26104
25931
|
}
|
|
26105
25932
|
|
|
26106
25933
|
static validate(userInput, rubric) {
|
|
26107
|
-
var numSelected =
|
|
25934
|
+
var numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
26108
25935
|
return sum + (selected ? 1 : 0);
|
|
26109
25936
|
}, 0);
|
|
26110
25937
|
|
|
@@ -26113,10 +25940,9 @@ class Radio$1 extends React.Component {
|
|
|
26113
25940
|
type: "invalid",
|
|
26114
25941
|
message: null
|
|
26115
25942
|
};
|
|
26116
|
-
}
|
|
26117
|
-
|
|
25943
|
+
}
|
|
26118
25944
|
|
|
26119
|
-
if (userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
25945
|
+
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
26120
25946
|
return {
|
|
26121
25947
|
type: "invalid",
|
|
26122
25948
|
message: i18n._("Please choose the correct number of answers.")
|
|
@@ -26129,14 +25955,12 @@ class Radio$1 extends React.Component {
|
|
|
26129
25955
|
message: i18n._("'None of the above' may not be selected " + "when other answers are selected.")
|
|
26130
25956
|
};
|
|
26131
25957
|
}
|
|
26132
|
-
/* jshint -W018 */
|
|
26133
25958
|
|
|
26134
|
-
|
|
26135
|
-
var correct = _.all(userInput.choicesSelected, (selected, i) => {
|
|
25959
|
+
var correct = userInput.choicesSelected.every((selected, i) => {
|
|
26136
25960
|
var isCorrect;
|
|
26137
25961
|
|
|
26138
25962
|
if (userInput.noneOfTheAboveIndex === i) {
|
|
26139
|
-
isCorrect =
|
|
25963
|
+
isCorrect = rubric.choices.every((choice, j) => {
|
|
26140
25964
|
return i === j || !choice.correct;
|
|
26141
25965
|
});
|
|
26142
25966
|
} else {
|
|
@@ -26145,9 +25969,6 @@ class Radio$1 extends React.Component {
|
|
|
26145
25969
|
|
|
26146
25970
|
return isCorrect === selected;
|
|
26147
25971
|
});
|
|
26148
|
-
/* jshint +W018 */
|
|
26149
|
-
|
|
26150
|
-
|
|
26151
25972
|
return {
|
|
26152
25973
|
type: "points",
|
|
26153
25974
|
earned: correct ? 1 : 0,
|
|
@@ -26169,9 +25990,7 @@ class Radio$1 extends React.Component {
|
|
|
26169
25990
|
var numCorrect = props.numCorrect;
|
|
26170
25991
|
|
|
26171
25992
|
for (var i = 0; i < choicesSelected.length; i++) {
|
|
26172
|
-
|
|
26173
|
-
var index = props.choices[i].originalIndex; // $FlowFixMe[incompatible-use]
|
|
26174
|
-
|
|
25993
|
+
var index = props.choices[i].originalIndex;
|
|
26175
25994
|
choicesSelected[index] = choiceStates[i].selected;
|
|
26176
25995
|
|
|
26177
25996
|
if (props.choices[i].isNoneOfTheAbove) {
|
|
@@ -26192,31 +26011,35 @@ class Radio$1 extends React.Component {
|
|
|
26192
26011
|
}; // Support legacy choiceState implementation
|
|
26193
26012
|
}
|
|
26194
26013
|
/* istanbul ignore if - props.values is deprecated */
|
|
26195
|
-
// $FlowFixMe[prop-missing]
|
|
26196
26014
|
|
|
26197
26015
|
|
|
26198
|
-
|
|
26016
|
+
var {
|
|
26017
|
+
values
|
|
26018
|
+
} = props;
|
|
26019
|
+
|
|
26020
|
+
if (values) {
|
|
26199
26021
|
var _noneOfTheAboveIndex = null;
|
|
26200
26022
|
var _noneOfTheAboveSelected = false;
|
|
26201
|
-
var
|
|
26023
|
+
var _choicesSelected = [...values];
|
|
26202
26024
|
var _countChoices = props.countChoices;
|
|
26203
26025
|
var _numCorrect = props.numCorrect;
|
|
26026
|
+
var valuesLength = values.length;
|
|
26204
26027
|
|
|
26205
|
-
for (var _i = 0; _i <
|
|
26028
|
+
for (var _i = 0; _i < valuesLength; _i++) {
|
|
26206
26029
|
var _index = props.choices[_i].originalIndex;
|
|
26207
|
-
|
|
26030
|
+
_choicesSelected[_index] = values[_i];
|
|
26208
26031
|
|
|
26209
26032
|
if (props.choices[_i].isNoneOfTheAbove) {
|
|
26210
26033
|
_noneOfTheAboveIndex = _index;
|
|
26211
26034
|
|
|
26212
|
-
if (
|
|
26035
|
+
if (_choicesSelected[_i]) {
|
|
26213
26036
|
_noneOfTheAboveSelected = true;
|
|
26214
26037
|
}
|
|
26215
26038
|
}
|
|
26216
26039
|
}
|
|
26217
26040
|
|
|
26218
26041
|
return {
|
|
26219
|
-
choicesSelected:
|
|
26042
|
+
choicesSelected: _choicesSelected,
|
|
26220
26043
|
noneOfTheAboveIndex: _noneOfTheAboveIndex,
|
|
26221
26044
|
noneOfTheAboveSelected: _noneOfTheAboveSelected,
|
|
26222
26045
|
countChoices: _countChoices,
|
|
@@ -26226,15 +26049,14 @@ class Radio$1 extends React.Component {
|
|
|
26226
26049
|
|
|
26227
26050
|
|
|
26228
26051
|
return {
|
|
26229
|
-
choicesSelected:
|
|
26052
|
+
choicesSelected: props.choices.map(() => false)
|
|
26230
26053
|
};
|
|
26231
26054
|
}
|
|
26232
26055
|
|
|
26233
26056
|
}
|
|
26234
26057
|
|
|
26235
26058
|
_defineProperty(Radio$1, "defaultProps", {
|
|
26236
|
-
choices: [
|
|
26237
|
-
displayCount: null,
|
|
26059
|
+
choices: [],
|
|
26238
26060
|
multipleSelect: false,
|
|
26239
26061
|
countChoices: false,
|
|
26240
26062
|
deselectEnabled: false,
|
|
@@ -26265,14 +26087,14 @@ var _choiceTransform = (widgetOptions, problemNum) => {
|
|
|
26265
26087
|
|
|
26266
26088
|
var _addNoneOfAbove = function _addNoneOfAbove(choices) {
|
|
26267
26089
|
var noneOfTheAbove = null;
|
|
26268
|
-
|
|
26269
|
-
var newChoices = _.reject(choices, function (choice, index) {
|
|
26090
|
+
var newChoices = choices.filter((choice, index) => {
|
|
26270
26091
|
if (choice.isNoneOfTheAbove) {
|
|
26271
26092
|
noneOfTheAbove = choice;
|
|
26272
|
-
return
|
|
26093
|
+
return false;
|
|
26273
26094
|
}
|
|
26274
|
-
}); // Place the "None of the above" options last
|
|
26275
26095
|
|
|
26096
|
+
return true;
|
|
26097
|
+
}); // Place the "None of the above" options last
|
|
26276
26098
|
|
|
26277
26099
|
if (noneOfTheAbove) {
|
|
26278
26100
|
newChoices.push(noneOfTheAbove);
|
|
@@ -26292,9 +26114,8 @@ var _choiceTransform = (widgetOptions, problemNum) => {
|
|
|
26292
26114
|
}; // Add meta-information to choices
|
|
26293
26115
|
|
|
26294
26116
|
|
|
26295
|
-
var choices = widgetOptions.choices.
|
|
26296
|
-
|
|
26297
|
-
return _.extend({}, choice, {
|
|
26117
|
+
var choices = widgetOptions.choices.map((choice, i) => {
|
|
26118
|
+
return _objectSpread2(_objectSpread2({}, choice), {}, {
|
|
26298
26119
|
originalIndex: i,
|
|
26299
26120
|
correct: Boolean(choice.correct)
|
|
26300
26121
|
});
|
|
@@ -26882,7 +26703,7 @@ class Definition extends React.Component {
|
|
|
26882
26703
|
}, /*#__PURE__*/React.createElement("span", {
|
|
26883
26704
|
className: "perseus-widget-definition"
|
|
26884
26705
|
}, /*#__PURE__*/React.createElement(Button, {
|
|
26885
|
-
size: "
|
|
26706
|
+
size: "medium",
|
|
26886
26707
|
kind: "tertiary",
|
|
26887
26708
|
onClick: () => {
|
|
26888
26709
|
this.props.trackInteraction();
|
|
@@ -29099,6 +28920,8 @@ class Group extends React.Component {
|
|
|
29099
28920
|
super(...arguments);
|
|
29100
28921
|
_this = this;
|
|
29101
28922
|
|
|
28923
|
+
_defineProperty(this, "rendererRef", void 0);
|
|
28924
|
+
|
|
29102
28925
|
_defineProperty(this, "change", function () {
|
|
29103
28926
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
29104
28927
|
args[_key] = arguments[_key];
|
|
@@ -29108,56 +28931,66 @@ class Group extends React.Component {
|
|
|
29108
28931
|
});
|
|
29109
28932
|
|
|
29110
28933
|
_defineProperty(this, "getUserInput", () => {
|
|
29111
|
-
|
|
29112
|
-
|
|
28934
|
+
var _this$rendererRef;
|
|
28935
|
+
|
|
28936
|
+
return (_this$rendererRef = this.rendererRef) === null || _this$rendererRef === void 0 ? void 0 : _this$rendererRef.getUserInput();
|
|
29113
28937
|
});
|
|
29114
28938
|
|
|
29115
28939
|
_defineProperty(this, "getSerializedState", () => {
|
|
29116
|
-
|
|
29117
|
-
|
|
28940
|
+
var _this$rendererRef2;
|
|
28941
|
+
|
|
28942
|
+
return (_this$rendererRef2 = this.rendererRef) === null || _this$rendererRef2 === void 0 ? void 0 : _this$rendererRef2.getSerializedState();
|
|
29118
28943
|
});
|
|
29119
28944
|
|
|
29120
28945
|
_defineProperty(this, "restoreSerializedState", (state, callback) => {
|
|
29121
|
-
|
|
29122
|
-
|
|
28946
|
+
var _this$rendererRef3;
|
|
28947
|
+
|
|
28948
|
+
(_this$rendererRef3 = this.rendererRef) === null || _this$rendererRef3 === void 0 ? void 0 : _this$rendererRef3.restoreSerializedState(state, callback); // Tell our renderer that we have no props to change
|
|
29123
28949
|
// (all our changes were in state):
|
|
29124
28950
|
|
|
29125
28951
|
return null;
|
|
29126
28952
|
});
|
|
29127
28953
|
|
|
29128
28954
|
_defineProperty(this, "simpleValidate", rubric => {
|
|
29129
|
-
|
|
29130
|
-
|
|
28955
|
+
var _this$rendererRef4;
|
|
28956
|
+
|
|
28957
|
+
return (_this$rendererRef4 = this.rendererRef) === null || _this$rendererRef4 === void 0 ? void 0 : _this$rendererRef4.score();
|
|
29131
28958
|
});
|
|
29132
28959
|
|
|
29133
28960
|
_defineProperty(this, "getInputPaths", () => {
|
|
29134
|
-
|
|
29135
|
-
|
|
28961
|
+
var _this$rendererRef5;
|
|
28962
|
+
|
|
28963
|
+
return (_this$rendererRef5 = this.rendererRef) === null || _this$rendererRef5 === void 0 ? void 0 : _this$rendererRef5.getInputPaths();
|
|
29136
28964
|
});
|
|
29137
28965
|
|
|
29138
|
-
_defineProperty(this, "setInputValue", (path, newValue,
|
|
29139
|
-
|
|
29140
|
-
|
|
28966
|
+
_defineProperty(this, "setInputValue", (path, newValue, callback) => {
|
|
28967
|
+
var _this$rendererRef6;
|
|
28968
|
+
|
|
28969
|
+
return (_this$rendererRef6 = this.rendererRef) === null || _this$rendererRef6 === void 0 ? void 0 : _this$rendererRef6.setInputValue(path, newValue, callback);
|
|
29141
28970
|
});
|
|
29142
28971
|
|
|
29143
28972
|
_defineProperty(this, "focus", () => {
|
|
29144
|
-
|
|
29145
|
-
|
|
28973
|
+
var _this$rendererRef7;
|
|
28974
|
+
|
|
28975
|
+
return (_this$rendererRef7 = this.rendererRef) === null || _this$rendererRef7 === void 0 ? void 0 : _this$rendererRef7.focus();
|
|
29146
28976
|
});
|
|
29147
28977
|
|
|
29148
28978
|
_defineProperty(this, "focusInputPath", path => {
|
|
29149
|
-
|
|
29150
|
-
|
|
28979
|
+
var _this$rendererRef8;
|
|
28980
|
+
|
|
28981
|
+
(_this$rendererRef8 = this.rendererRef) === null || _this$rendererRef8 === void 0 ? void 0 : _this$rendererRef8.focusPath(path);
|
|
29151
28982
|
});
|
|
29152
28983
|
|
|
29153
28984
|
_defineProperty(this, "blurInputPath", path => {
|
|
29154
|
-
|
|
29155
|
-
|
|
28985
|
+
var _this$rendererRef9;
|
|
28986
|
+
|
|
28987
|
+
(_this$rendererRef9 = this.rendererRef) === null || _this$rendererRef9 === void 0 ? void 0 : _this$rendererRef9.blurPath(path);
|
|
29156
28988
|
});
|
|
29157
28989
|
|
|
29158
28990
|
_defineProperty(this, "showRationalesForCurrentlySelectedChoices", () => {
|
|
29159
|
-
|
|
29160
|
-
|
|
28991
|
+
var _this$rendererRef10;
|
|
28992
|
+
|
|
28993
|
+
(_this$rendererRef10 = this.rendererRef) === null || _this$rendererRef10 === void 0 ? void 0 : _this$rendererRef10.showRationalesForCurrentlySelectedChoices();
|
|
29161
28994
|
});
|
|
29162
28995
|
}
|
|
29163
28996
|
|
|
@@ -29169,7 +29002,9 @@ class Group extends React.Component {
|
|
|
29169
29002
|
}
|
|
29170
29003
|
|
|
29171
29004
|
render() {
|
|
29172
|
-
var
|
|
29005
|
+
var _this$rendererRef11;
|
|
29006
|
+
|
|
29007
|
+
var apiOptions = _objectSpread2(_objectSpread2(_objectSpread2({}, ApiOptions.defaults), this.props.apiOptions), {}, {
|
|
29173
29008
|
// Api Rewriting to support correct onFocus/onBlur
|
|
29174
29009
|
// events for the mobile API
|
|
29175
29010
|
onFocusChange: (newFocus, oldFocus) => {
|
|
@@ -29191,8 +29026,8 @@ class Group extends React.Component {
|
|
|
29191
29026
|
// really we should have a more unidirectional flow. TODO(marcia): fix.
|
|
29192
29027
|
|
|
29193
29028
|
|
|
29194
|
-
var
|
|
29195
|
-
|
|
29029
|
+
var groupWidgets = this.props.findWidgets("group");
|
|
29030
|
+
var number = groupWidgets.indexOf(this);
|
|
29196
29031
|
var problemNumComponent = this.props.apiOptions.groupAnnotator(number, this.props.widgetId); // This is a little strange because the id of the widget that actually
|
|
29197
29032
|
// changed is going to be lost in favor of the group widget's id. The
|
|
29198
29033
|
// widgets prop also wasn't actually changed, and this only serves to
|
|
@@ -29200,15 +29035,12 @@ class Group extends React.Component {
|
|
|
29200
29035
|
// has occurred.
|
|
29201
29036
|
|
|
29202
29037
|
var onInteractWithWidget = id => {
|
|
29203
|
-
|
|
29204
|
-
|
|
29205
|
-
// eslint-disable-next-line react/no-string-refs
|
|
29206
|
-
this.change("widgets", this.refs.renderer.props.widgets);
|
|
29038
|
+
if (this.rendererRef) {
|
|
29039
|
+
this.change("widgets", this.rendererRef.props.widgets);
|
|
29207
29040
|
}
|
|
29208
|
-
};
|
|
29209
|
-
|
|
29041
|
+
};
|
|
29210
29042
|
|
|
29211
|
-
var score = this.
|
|
29043
|
+
var score = (_this$rendererRef11 = this.rendererRef) === null || _this$rendererRef11 === void 0 ? void 0 : _this$rendererRef11.score();
|
|
29212
29044
|
var isValid = score && score.type !== "invalid";
|
|
29213
29045
|
var isInvalid = score && score.type === "invalid"; // TODO(mdr): Widgets inside this Renderer are not discoverable through
|
|
29214
29046
|
// the parent Renderer's `findWidgets` function.
|
|
@@ -29219,15 +29051,17 @@ class Group extends React.Component {
|
|
|
29219
29051
|
"perseus-group-valid-answer": isValid,
|
|
29220
29052
|
"perseus-group-invalid-answer": isInvalid
|
|
29221
29053
|
})
|
|
29222
|
-
}, problemNumComponent, /*#__PURE__*/React.createElement(Renderer,
|
|
29223
|
-
|
|
29224
|
-
|
|
29054
|
+
}, problemNumComponent, /*#__PURE__*/React.createElement(Renderer, {
|
|
29055
|
+
content: this.props.content,
|
|
29056
|
+
widgets: this.props.widgets,
|
|
29057
|
+
images: this.props.images,
|
|
29058
|
+
ref: _ref => this.rendererRef = _ref,
|
|
29225
29059
|
apiOptions: apiOptions,
|
|
29226
29060
|
findExternalWidgets: this.props.findWidgets,
|
|
29227
29061
|
reviewMode: !!this.props.reviewModeRubric,
|
|
29228
29062
|
onInteractWithWidget: onInteractWithWidget,
|
|
29229
29063
|
linterContext: this.props.linterContext
|
|
29230
|
-
})
|
|
29064
|
+
}), this.props.icon && /*#__PURE__*/React.createElement("div", {
|
|
29231
29065
|
className: "group-icon"
|
|
29232
29066
|
}, this.props.icon));
|
|
29233
29067
|
}
|
|
@@ -29242,7 +29076,7 @@ _defineProperty(Group, "defaultProps", {
|
|
|
29242
29076
|
});
|
|
29243
29077
|
|
|
29244
29078
|
var traverseChildWidgets$1 = function traverseChildWidgets(props, traverseRenderer) {
|
|
29245
|
-
return
|
|
29079
|
+
return _objectSpread2(_objectSpread2({}, props), traverseRenderer(props));
|
|
29246
29080
|
};
|
|
29247
29081
|
|
|
29248
29082
|
var Group$1 = {
|
|
@@ -41132,43 +40966,23 @@ var styles$9 = StyleSheet.create({
|
|
|
41132
40966
|
}
|
|
41133
40967
|
});
|
|
41134
40968
|
|
|
41135
|
-
var getLineHeightForNode = (
|
|
41136
|
-
return $line2.offset().top - $line1.offset().top;
|
|
40969
|
+
var getLineHeightForNode = (line1, line2) => {
|
|
40970
|
+
return $(line2).offset().top - $(line1).offset().top;
|
|
41137
40971
|
};
|
|
41138
40972
|
|
|
41139
|
-
|
|
41140
|
-
|
|
41141
|
-
|
|
41142
|
-
|
|
41143
|
-
|
|
41144
|
-
|
|
41145
|
-
|
|
41146
|
-
|
|
41147
|
-
|
|
41148
|
-
|
|
41149
|
-
|
|
41150
|
-
|
|
41151
|
-
}
|
|
41152
|
-
var SQUARE_LABEL_STYLE = {
|
|
41153
|
-
display: "inline-block",
|
|
41154
|
-
color: "rgb(255, 255, 255)",
|
|
41155
|
-
backgroundColor: "rgb(90, 90, 90)",
|
|
41156
|
-
paddingLeft: 10,
|
|
41157
|
-
paddingRight: 10,
|
|
41158
|
-
userSelect: "none",
|
|
41159
|
-
WebkitUserSelect: "none"
|
|
41160
|
-
};
|
|
41161
|
-
var CIRCLE_LABEL_STYLE = {
|
|
41162
|
-
display: "inline-block",
|
|
41163
|
-
color: "rgb(255, 255, 255)",
|
|
41164
|
-
backgroundColor: "rgb(90, 90, 90)",
|
|
41165
|
-
userSelect: "none",
|
|
41166
|
-
WebkitUserSelect: "none",
|
|
41167
|
-
width: 22,
|
|
41168
|
-
height: 22,
|
|
41169
|
-
borderRadius: "50%",
|
|
41170
|
-
textAlign: "center"
|
|
41171
|
-
};
|
|
40973
|
+
function getInitialParseState() {
|
|
40974
|
+
return {
|
|
40975
|
+
currentRef: [],
|
|
40976
|
+
useRefs: true,
|
|
40977
|
+
lastRef: 0,
|
|
40978
|
+
firstSentenceRef: null,
|
|
40979
|
+
firstQuestionRef: null,
|
|
40980
|
+
lastFootnote: {
|
|
40981
|
+
id: 0,
|
|
40982
|
+
text: ""
|
|
40983
|
+
}
|
|
40984
|
+
};
|
|
40985
|
+
}
|
|
41172
40986
|
|
|
41173
40987
|
class RefStart extends React.Component {
|
|
41174
40988
|
constructor() {
|
|
@@ -41187,10 +41001,6 @@ class RefStart extends React.Component {
|
|
|
41187
41001
|
|
|
41188
41002
|
}
|
|
41189
41003
|
|
|
41190
|
-
_defineProperty(RefStart, "propTypes", {
|
|
41191
|
-
refContent: PropTypes.node.isRequired
|
|
41192
|
-
});
|
|
41193
|
-
|
|
41194
41004
|
class RefEnd extends React.Component {
|
|
41195
41005
|
render() {
|
|
41196
41006
|
return /*#__PURE__*/React.createElement("span", {
|
|
@@ -41201,20 +41011,11 @@ class RefEnd extends React.Component {
|
|
|
41201
41011
|
}
|
|
41202
41012
|
|
|
41203
41013
|
var rules = {
|
|
41204
|
-
// $FlowFixMe[prop-missing]
|
|
41205
|
-
// $FlowFixMe[incompatible-use]
|
|
41206
41014
|
newline: SimpleMarkdown.defaultRules.newline,
|
|
41207
|
-
// $FlowFixMe[prop-missing]
|
|
41208
|
-
// $FlowFixMe[incompatible-use]
|
|
41209
41015
|
paragraph: SimpleMarkdown.defaultRules.paragraph,
|
|
41210
|
-
// $FlowFixMe[prop-missing]
|
|
41211
|
-
// $FlowFixMe[incompatible-use]
|
|
41212
41016
|
escape: SimpleMarkdown.defaultRules.escape,
|
|
41213
41017
|
passageFootnote: {
|
|
41214
|
-
// $FlowFixMe[prop-missing]
|
|
41215
|
-
// $FlowFixMe[incompatible-use]
|
|
41216
41018
|
order: SimpleMarkdown.defaultRules.escape.order + 0.1,
|
|
41217
|
-
// $FlowFixMe[prop-missing]
|
|
41218
41019
|
match: SimpleMarkdown.inlineRegex(/^\^/),
|
|
41219
41020
|
parse: (capture, _parse, state) => {
|
|
41220
41021
|
// if no footnotes have been seen, we're id 1. otherwise,
|
|
@@ -41243,10 +41044,8 @@ var rules = {
|
|
|
41243
41044
|
}
|
|
41244
41045
|
},
|
|
41245
41046
|
refStart: {
|
|
41246
|
-
// $FlowFixMe[prop-missing]
|
|
41247
|
-
// $FlowFixMe[incompatible-use]
|
|
41248
41047
|
order: SimpleMarkdown.defaultRules.escape.order + 0.2,
|
|
41249
|
-
match: function match(source
|
|
41048
|
+
match: function match(source) {
|
|
41250
41049
|
var capture = /^\{\{/.exec(source);
|
|
41251
41050
|
|
|
41252
41051
|
if (capture) {
|
|
@@ -41277,12 +41076,14 @@ var rules = {
|
|
|
41277
41076
|
}
|
|
41278
41077
|
|
|
41279
41078
|
closeIndex++;
|
|
41280
|
-
}
|
|
41281
|
-
|
|
41282
|
-
var refText = source.slice(2, closeIndex); // A "magic" capture that matches the opening {{
|
|
41079
|
+
} // A "magic" capture that matches the opening {{
|
|
41283
41080
|
// but captures the full ref text internally :D
|
|
41081
|
+
// unfortunately the "magic" makes Flow upset,
|
|
41082
|
+
// so there's this questionable workaround
|
|
41284
41083
|
|
|
41285
|
-
|
|
41084
|
+
|
|
41085
|
+
capture[1] = source.slice(2, closeIndex);
|
|
41086
|
+
return capture;
|
|
41286
41087
|
}
|
|
41287
41088
|
|
|
41288
41089
|
return null;
|
|
@@ -41306,7 +41107,7 @@ var rules = {
|
|
|
41306
41107
|
// to make those refs into react refs on the
|
|
41307
41108
|
// passage, for instance!
|
|
41308
41109
|
useRefs: false
|
|
41309
|
-
},
|
|
41110
|
+
}, getInitialParseState()));
|
|
41310
41111
|
|
|
41311
41112
|
return {
|
|
41312
41113
|
ref: ref,
|
|
@@ -41314,26 +41115,27 @@ var rules = {
|
|
|
41314
41115
|
};
|
|
41315
41116
|
},
|
|
41316
41117
|
react: (node, output, state) => {
|
|
41317
|
-
|
|
41118
|
+
var ref = node.ref;
|
|
41119
|
+
|
|
41120
|
+
if (ref == null) {
|
|
41318
41121
|
return null;
|
|
41319
41122
|
} // We don't pass state here because this is parsed
|
|
41320
41123
|
// and output out-of-band. We don't want to affect
|
|
41321
41124
|
// our state by the double-output here :).
|
|
41322
41125
|
|
|
41323
41126
|
|
|
41324
|
-
var refContent = output(node.refContent, {});
|
|
41127
|
+
var refContent = output(node.refContent, {}); // note(matthewc) the refs created here become the refs
|
|
41128
|
+
// pulled from `this.refs` in passage.jsx
|
|
41129
|
+
|
|
41325
41130
|
return /*#__PURE__*/React.createElement(RefStart, {
|
|
41326
|
-
ref: START_REF_PREFIX +
|
|
41327
|
-
key: START_REF_PREFIX +
|
|
41131
|
+
ref: START_REF_PREFIX + ref,
|
|
41132
|
+
key: START_REF_PREFIX + ref,
|
|
41328
41133
|
refContent: refContent
|
|
41329
41134
|
});
|
|
41330
41135
|
}
|
|
41331
41136
|
},
|
|
41332
41137
|
refEnd: {
|
|
41333
|
-
// $FlowFixMe[prop-missing]
|
|
41334
|
-
// $FlowFixMe[incompatible-use]
|
|
41335
41138
|
order: SimpleMarkdown.defaultRules.escape.order + 0.3,
|
|
41336
|
-
// $FlowFixMe[prop-missing]
|
|
41337
41139
|
match: SimpleMarkdown.inlineRegex(/^\}\}/),
|
|
41338
41140
|
parse: (capture, _parse3, state) => {
|
|
41339
41141
|
if (!state.useRefs) {
|
|
@@ -41349,6 +41151,8 @@ var rules = {
|
|
|
41349
41151
|
},
|
|
41350
41152
|
react: (node, output, state) => {
|
|
41351
41153
|
if (node.ref != null) {
|
|
41154
|
+
// note(matthewc) the refs created here become the refs
|
|
41155
|
+
// pulled from `this.refs` in passage.jsx
|
|
41352
41156
|
return /*#__PURE__*/React.createElement(RefEnd, {
|
|
41353
41157
|
ref: END_REF_PREFIX + node.ref,
|
|
41354
41158
|
key: END_REF_PREFIX + node.ref
|
|
@@ -41362,10 +41166,7 @@ var rules = {
|
|
|
41362
41166
|
}
|
|
41363
41167
|
},
|
|
41364
41168
|
squareLabel: {
|
|
41365
|
-
// $FlowFixMe[prop-missing]
|
|
41366
|
-
// $FlowFixMe[incompatible-use]
|
|
41367
41169
|
order: SimpleMarkdown.defaultRules.escape.order + 0.4,
|
|
41368
|
-
// $FlowFixMe[prop-missing]
|
|
41369
41170
|
match: SimpleMarkdown.inlineRegex(/^\[\[(\w+)\]\]( *)/),
|
|
41370
41171
|
parse: (capture, _parse4, state) => {
|
|
41371
41172
|
if (!state.firstQuestionRef) {
|
|
@@ -41394,10 +41195,7 @@ var rules = {
|
|
|
41394
41195
|
}
|
|
41395
41196
|
},
|
|
41396
41197
|
circleLabel: {
|
|
41397
|
-
// $FlowFixMe[prop-missing]
|
|
41398
|
-
// $FlowFixMe[incompatible-use]
|
|
41399
41198
|
order: SimpleMarkdown.defaultRules.escape.order + 0.5,
|
|
41400
|
-
// $FlowFixMe[prop-missing]
|
|
41401
41199
|
match: SimpleMarkdown.inlineRegex(/^\(\((\w+)\)\)( *)/),
|
|
41402
41200
|
parse: (capture, _parse5, state) => {
|
|
41403
41201
|
return {
|
|
@@ -41422,10 +41220,7 @@ var rules = {
|
|
|
41422
41220
|
}
|
|
41423
41221
|
},
|
|
41424
41222
|
squareBracketRef: {
|
|
41425
|
-
// $FlowFixMe[prop-missing]
|
|
41426
|
-
// $FlowFixMe[incompatible-use]
|
|
41427
41223
|
order: SimpleMarkdown.defaultRules.escape.order + 0.6,
|
|
41428
|
-
// $FlowFixMe[prop-missing]
|
|
41429
41224
|
match: SimpleMarkdown.inlineRegex(/^\[(\d+)\]( *)/),
|
|
41430
41225
|
parse: (capture, _parse6, state) => {
|
|
41431
41226
|
if (!state.firstSentenceRef) {
|
|
@@ -41451,10 +41246,7 @@ var rules = {
|
|
|
41451
41246
|
}
|
|
41452
41247
|
},
|
|
41453
41248
|
highlight: {
|
|
41454
|
-
// $FlowFixMe[prop-missing]
|
|
41455
|
-
// $FlowFixMe[incompatible-use]
|
|
41456
41249
|
order: SimpleMarkdown.defaultRules.escape.order + 0.7,
|
|
41457
|
-
// $FlowFixMe[prop-missing]
|
|
41458
41250
|
match: SimpleMarkdown.inlineRegex(/^{highlighting.start}(.+?){highlighting.end}/),
|
|
41459
41251
|
parse: (capture, _parse7, state) => {
|
|
41460
41252
|
return {
|
|
@@ -41469,10 +41261,7 @@ var rules = {
|
|
|
41469
41261
|
}
|
|
41470
41262
|
},
|
|
41471
41263
|
reviewHighlight: {
|
|
41472
|
-
// $FlowFixMe[prop-missing]
|
|
41473
|
-
// $FlowFixMe[incompatible-use]
|
|
41474
41264
|
order: SimpleMarkdown.defaultRules.escape.order + 0.7,
|
|
41475
|
-
// $FlowFixMe[prop-missing]
|
|
41476
41265
|
match: SimpleMarkdown.inlineRegex(/^{review-highlighting.start}(.+?){review-highlighting.end}/),
|
|
41477
41266
|
parse: (capture, _parse8, state) => {
|
|
41478
41267
|
return {
|
|
@@ -41486,17 +41275,9 @@ var rules = {
|
|
|
41486
41275
|
}, node.content)];
|
|
41487
41276
|
}
|
|
41488
41277
|
},
|
|
41489
|
-
// $FlowFixMe[prop-missing]
|
|
41490
|
-
// $FlowFixMe[incompatible-use]
|
|
41491
41278
|
strong: SimpleMarkdown.defaultRules.strong,
|
|
41492
|
-
// $FlowFixMe[prop-missing]
|
|
41493
|
-
// $FlowFixMe[incompatible-use]
|
|
41494
41279
|
u: SimpleMarkdown.defaultRules.u,
|
|
41495
|
-
// $FlowFixMe[prop-missing]
|
|
41496
|
-
// $FlowFixMe[incompatible-use]
|
|
41497
41280
|
em: SimpleMarkdown.defaultRules.em,
|
|
41498
|
-
// $FlowFixMe[prop-missing]
|
|
41499
|
-
// $FlowFixMe[incompatible-use]
|
|
41500
41281
|
del: SimpleMarkdown.defaultRules.del,
|
|
41501
41282
|
text: _objectSpread2(_objectSpread2({}, SimpleMarkdown.defaultRules.text), {}, {
|
|
41502
41283
|
react(node, output, state) {
|
|
@@ -41507,32 +41288,54 @@ var rules = {
|
|
|
41507
41288
|
|
|
41508
41289
|
})
|
|
41509
41290
|
};
|
|
41510
|
-
var
|
|
41511
|
-
|
|
41512
|
-
|
|
41513
|
-
|
|
41514
|
-
|
|
41515
|
-
|
|
41516
|
-
|
|
41517
|
-
|
|
41518
|
-
|
|
41519
|
-
|
|
41291
|
+
var START_REF_PREFIX = "start-ref-";
|
|
41292
|
+
var END_REF_PREFIX = "end-ref-";
|
|
41293
|
+
var REF_STYLE = {
|
|
41294
|
+
display: "inline-block",
|
|
41295
|
+
width: 0,
|
|
41296
|
+
visibility: "hidden"
|
|
41297
|
+
};
|
|
41298
|
+
var LABEL_OUTER_STYLE = {
|
|
41299
|
+
// for some reason we need these to keep the nbsp from wrapping when the
|
|
41300
|
+
// inner circle/square is display: inline-block
|
|
41301
|
+
display: "inline",
|
|
41302
|
+
whiteSpace: "nowrap"
|
|
41303
|
+
};
|
|
41304
|
+
var SQUARE_LABEL_STYLE = {
|
|
41305
|
+
display: "inline-block",
|
|
41306
|
+
color: "rgb(255, 255, 255)",
|
|
41307
|
+
backgroundColor: "rgb(90, 90, 90)",
|
|
41308
|
+
paddingLeft: 10,
|
|
41309
|
+
paddingRight: 10,
|
|
41310
|
+
userSelect: "none",
|
|
41311
|
+
WebkitUserSelect: "none"
|
|
41312
|
+
};
|
|
41313
|
+
var CIRCLE_LABEL_STYLE = {
|
|
41314
|
+
display: "inline-block",
|
|
41315
|
+
color: "rgb(255, 255, 255)",
|
|
41316
|
+
backgroundColor: "rgb(90, 90, 90)",
|
|
41317
|
+
userSelect: "none",
|
|
41318
|
+
WebkitUserSelect: "none",
|
|
41319
|
+
width: 22,
|
|
41320
|
+
height: 22,
|
|
41321
|
+
borderRadius: "50%",
|
|
41322
|
+
textAlign: "center"
|
|
41323
|
+
};
|
|
41520
41324
|
var builtParser = SimpleMarkdown.parserFor(rules);
|
|
41521
41325
|
|
|
41522
41326
|
var parse = (source, state) => {
|
|
41523
41327
|
state = state || {};
|
|
41524
41328
|
var paragraphedSource = source + "\n\n";
|
|
41525
|
-
return builtParser(paragraphedSource, _.extend(state,
|
|
41329
|
+
return builtParser(paragraphedSource, _.extend(state, getInitialParseState()));
|
|
41526
41330
|
};
|
|
41527
41331
|
|
|
41332
|
+
var output = SimpleMarkdown.reactFor(SimpleMarkdown.ruleOutput(rules, "react"));
|
|
41528
41333
|
var PassageMarkdown = {
|
|
41529
|
-
parse
|
|
41530
|
-
|
|
41531
|
-
|
|
41532
|
-
|
|
41533
|
-
|
|
41534
|
-
END_REF_PREFIX: END_REF_PREFIX,
|
|
41535
|
-
_rulesForTesting: rules
|
|
41334
|
+
parse,
|
|
41335
|
+
output,
|
|
41336
|
+
START_REF_PREFIX,
|
|
41337
|
+
END_REF_PREFIX,
|
|
41338
|
+
getInitialParseState
|
|
41536
41339
|
};
|
|
41537
41340
|
|
|
41538
41341
|
// A fake paragraph to measure the line height of the passage. In CSS we always
|
|
@@ -41545,9 +41348,9 @@ class LineHeightMeasurer extends React.Component {
|
|
|
41545
41348
|
|
|
41546
41349
|
_defineProperty(this, "_cachedLineHeight", void 0);
|
|
41547
41350
|
|
|
41548
|
-
_defineProperty(this, "
|
|
41351
|
+
_defineProperty(this, "body", void 0);
|
|
41549
41352
|
|
|
41550
|
-
_defineProperty(this, "
|
|
41353
|
+
_defineProperty(this, "end", void 0);
|
|
41551
41354
|
}
|
|
41552
41355
|
|
|
41553
41356
|
measureLineHeight() {
|
|
@@ -41559,23 +41362,30 @@ class LineHeightMeasurer extends React.Component {
|
|
|
41559
41362
|
}
|
|
41560
41363
|
|
|
41561
41364
|
forceMeasureLineHeight() {
|
|
41562
|
-
|
|
41563
|
-
this
|
|
41365
|
+
var body = this.body;
|
|
41366
|
+
var end = this.end;
|
|
41367
|
+
|
|
41368
|
+
if (!body || !end) {
|
|
41369
|
+
return;
|
|
41370
|
+
} // Add some text which magically fills an entire line.
|
|
41371
|
+
|
|
41372
|
+
|
|
41373
|
+
body.textContent = " \u0080"; // Now, the line height is the difference between the top of the
|
|
41564
41374
|
// second line and the top of the first line.
|
|
41565
41375
|
|
|
41566
|
-
this._cachedLineHeight = getLineHeightForNode(
|
|
41376
|
+
this._cachedLineHeight = getLineHeightForNode(body, end); // Clear out the first line so it doesn't overlap the passage.
|
|
41567
41377
|
|
|
41568
|
-
|
|
41378
|
+
body.textContent = "";
|
|
41569
41379
|
}
|
|
41570
41380
|
|
|
41571
41381
|
render() {
|
|
41572
41382
|
return /*#__PURE__*/React.createElement("div", {
|
|
41573
41383
|
className: css(styles$8.measurer)
|
|
41574
41384
|
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
|
41575
|
-
ref:
|
|
41385
|
+
ref: _ref => this.body = _ref,
|
|
41576
41386
|
className: "paragraph"
|
|
41577
41387
|
}), /*#__PURE__*/React.createElement("div", {
|
|
41578
|
-
ref:
|
|
41388
|
+
ref: _ref2 => this.end = _ref2
|
|
41579
41389
|
})));
|
|
41580
41390
|
}
|
|
41581
41391
|
|
|
@@ -41594,9 +41404,11 @@ class Passage extends React.Component {
|
|
|
41594
41404
|
constructor() {
|
|
41595
41405
|
super(...arguments);
|
|
41596
41406
|
|
|
41597
|
-
_defineProperty(this, "
|
|
41407
|
+
_defineProperty(this, "_contentRef", void 0);
|
|
41598
41408
|
|
|
41599
|
-
_defineProperty(this, "
|
|
41409
|
+
_defineProperty(this, "_lineHeightMeasurerRef", void 0);
|
|
41410
|
+
|
|
41411
|
+
_defineProperty(this, "_onResize", void 0);
|
|
41600
41412
|
|
|
41601
41413
|
_defineProperty(this, "_stylesAppiedTimer", void 0);
|
|
41602
41414
|
|
|
@@ -41617,6 +41429,8 @@ class Passage extends React.Component {
|
|
|
41617
41429
|
this._updateState();
|
|
41618
41430
|
|
|
41619
41431
|
this._onResize = _.throttle(() => {
|
|
41432
|
+
var _this$_lineHeightMeas;
|
|
41433
|
+
|
|
41620
41434
|
// If we're rendering JIPT text, we won't have line numbers or a
|
|
41621
41435
|
// line height measurer, so skip handling this resize.
|
|
41622
41436
|
if (this.shouldRenderJipt()) {
|
|
@@ -41627,7 +41441,7 @@ class Passage extends React.Component {
|
|
|
41627
41441
|
// listen for resize events.
|
|
41628
41442
|
|
|
41629
41443
|
|
|
41630
|
-
this.
|
|
41444
|
+
(_this$_lineHeightMeas = this._lineHeightMeasurerRef) === null || _this$_lineHeightMeas === void 0 ? void 0 : _this$_lineHeightMeas.forceMeasureLineHeight();
|
|
41631
41445
|
|
|
41632
41446
|
this._updateState();
|
|
41633
41447
|
}, 500);
|
|
@@ -41690,9 +41504,8 @@ class Passage extends React.Component {
|
|
|
41690
41504
|
}
|
|
41691
41505
|
|
|
41692
41506
|
_measureLines() {
|
|
41693
|
-
|
|
41694
|
-
var
|
|
41695
|
-
var contentsHeight = $renderer.height();
|
|
41507
|
+
var renderer = ReactDOM__default.findDOMNode(this._contentRef);
|
|
41508
|
+
var contentsHeight = $(renderer).height();
|
|
41696
41509
|
|
|
41697
41510
|
var lineHeight = this._getLineHeight();
|
|
41698
41511
|
|
|
@@ -41719,7 +41532,9 @@ class Passage extends React.Component {
|
|
|
41719
41532
|
}
|
|
41720
41533
|
|
|
41721
41534
|
_getLineHeight() {
|
|
41722
|
-
|
|
41535
|
+
var _this$_lineHeightMeas2;
|
|
41536
|
+
|
|
41537
|
+
return ((_this$_lineHeightMeas2 = this._lineHeightMeasurerRef) === null || _this$_lineHeightMeas2 === void 0 ? void 0 : _this$_lineHeightMeas2.measureLineHeight()) || 0;
|
|
41723
41538
|
}
|
|
41724
41539
|
|
|
41725
41540
|
getLineCount() {
|
|
@@ -41735,12 +41550,16 @@ class Passage extends React.Component {
|
|
|
41735
41550
|
* These are functions to support the passage refs inter-widgets feature
|
|
41736
41551
|
* where other widgets can fetch the line numbers of a reference inside of
|
|
41737
41552
|
* a passage.
|
|
41553
|
+
*
|
|
41554
|
+
* todo(matthewc): The refs are created by PassageMarkdown's refStart and refEnd,
|
|
41555
|
+
* somehow bubbling up to Passage's `this.refs`. This runs against
|
|
41556
|
+
* current best practices for refs by using string refs, but also
|
|
41557
|
+
* by breaking our expectation of explicit data flow.
|
|
41738
41558
|
*/
|
|
41739
41559
|
|
|
41740
41560
|
|
|
41741
41561
|
_getStartRefLineNumber(referenceNumber) {
|
|
41742
|
-
var refRef = PassageMarkdown.START_REF_PREFIX + referenceNumber;
|
|
41743
|
-
|
|
41562
|
+
var refRef = PassageMarkdown.START_REF_PREFIX + referenceNumber;
|
|
41744
41563
|
var ref = this.refs[refRef];
|
|
41745
41564
|
|
|
41746
41565
|
if (!ref) {
|
|
@@ -41763,8 +41582,7 @@ class Passage extends React.Component {
|
|
|
41763
41582
|
}
|
|
41764
41583
|
|
|
41765
41584
|
_getEndRefLineNumber(referenceNumber) {
|
|
41766
|
-
var refRef = PassageMarkdown.END_REF_PREFIX + referenceNumber;
|
|
41767
|
-
|
|
41585
|
+
var refRef = PassageMarkdown.END_REF_PREFIX + referenceNumber;
|
|
41768
41586
|
var ref = this.refs[refRef];
|
|
41769
41587
|
|
|
41770
41588
|
if (!ref) {
|
|
@@ -41801,9 +41619,8 @@ class Passage extends React.Component {
|
|
|
41801
41619
|
}
|
|
41802
41620
|
|
|
41803
41621
|
_convertPosToLineNumber(absoluteVPos) {
|
|
41804
|
-
|
|
41805
|
-
var
|
|
41806
|
-
var relativeVPos = absoluteVPos - $content.offset().top;
|
|
41622
|
+
var content = ReactDOM__default.findDOMNode(this._contentRef);
|
|
41623
|
+
var relativeVPos = absoluteVPos - $(content).offset().top;
|
|
41807
41624
|
|
|
41808
41625
|
var lineHeight = this._getLineHeight();
|
|
41809
41626
|
|
|
@@ -41812,8 +41629,7 @@ class Passage extends React.Component {
|
|
|
41812
41629
|
}
|
|
41813
41630
|
|
|
41814
41631
|
_getRefContent(referenceNumber) {
|
|
41815
|
-
var refRef = PassageMarkdown.START_REF_PREFIX + referenceNumber;
|
|
41816
|
-
|
|
41632
|
+
var refRef = PassageMarkdown.START_REF_PREFIX + referenceNumber;
|
|
41817
41633
|
var ref = this.refs[refRef];
|
|
41818
41634
|
|
|
41819
41635
|
if (!ref) {
|
|
@@ -41919,12 +41735,9 @@ class Passage extends React.Component {
|
|
|
41919
41735
|
onSerializedHighlightsUpdate: this._handleSerializedHighlightsUpdate,
|
|
41920
41736
|
serializedHighlights: this.props.highlights
|
|
41921
41737
|
}, /*#__PURE__*/React.createElement("div", {
|
|
41922
|
-
ref:
|
|
41923
|
-
}, /*#__PURE__*/React.createElement(LineHeightMeasurer
|
|
41924
|
-
|
|
41925
|
-
// $FlowFixMe[incompatible-type](0.52.0->0.53.0)
|
|
41926
|
-
, {
|
|
41927
|
-
ref: e => this._lineHeightMeasurer = e
|
|
41738
|
+
ref: _ref4 => this._contentRef = _ref4
|
|
41739
|
+
}, /*#__PURE__*/React.createElement(LineHeightMeasurer, {
|
|
41740
|
+
ref: _ref3 => this._lineHeightMeasurerRef = _ref3
|
|
41928
41741
|
}), PassageMarkdown.output(parsed)));
|
|
41929
41742
|
}
|
|
41930
41743
|
|
|
@@ -41967,10 +41780,7 @@ class Passage extends React.Component {
|
|
|
41967
41780
|
});
|
|
41968
41781
|
}
|
|
41969
41782
|
|
|
41970
|
-
var parseState =
|
|
41971
|
-
firstSentenceRef: null,
|
|
41972
|
-
firstQuestionRef: null
|
|
41973
|
-
}; // Replace the vertical double quote characters quoting text with
|
|
41783
|
+
var parseState = PassageMarkdown.getInitialParseState(); // Replace the vertical double quote characters quoting text with
|
|
41974
41784
|
// an unicode left and right double quote characters. This would
|
|
41975
41785
|
// avoid rendering right or left unicode double quotes on both
|
|
41976
41786
|
// ends of a sentence wrapped by double quotes.
|