@khanacademy/perseus 17.0.0 → 17.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/CHANGELOG.md +59 -0
- package/dist/dependencies.d.ts +1 -0
- package/dist/es/index.css +20 -0
- package/dist/es/index.js +443 -338
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +20 -0
- package/dist/index.js +444 -340
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +1 -0
- package/dist/strings.js +1 -0
- package/dist/widgets/label-image/answer-pill.d.ts +10 -0
- package/dist/widgets/label-image/hide-answers-toggle.d.ts +6 -0
- package/dist/widgets/label-image/marker.d.ts +6 -9
- package/dist/widgets/label-image/types.d.ts +1 -0
- package/dist/widgets/label-image.d.ts +101 -12
- package/package.json +30 -22
- package/src/__tests__/__snapshots__/renderer.test.tsx.snap +3 -3
- package/src/dependencies.ts +4 -0
- package/src/perseus-types.ts +2 -0
- package/src/styles/widgets/label-image.less +32 -0
- package/src/widgets/__stories__/label-image.stories.tsx +73 -3
- package/src/widgets/__testdata__/label-image.testdata.ts +118 -1
- package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +2 -2
- package/src/widgets/label-image/__stories__/answer-pill.stories.tsx +62 -0
- package/src/widgets/label-image/__tests__/answer-pill.test.tsx +83 -0
- package/src/widgets/label-image/__tests__/hide-answers-toggle.test.tsx +39 -0
- package/src/widgets/label-image/answer-choices.tsx +7 -1
- package/src/widgets/label-image/answer-pill.tsx +89 -0
- package/src/widgets/label-image/hide-answers-toggle.tsx +40 -0
- package/src/widgets/label-image/marker.tsx +116 -174
- package/src/widgets/label-image/types.ts +1 -0
- package/src/widgets/label-image.test.ts +142 -52
- package/src/widgets/label-image.tsx +212 -135
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/index.js
CHANGED
|
@@ -28,6 +28,10 @@ var Button = require('@khanacademy/wonder-blocks-button');
|
|
|
28
28
|
var wonderBlocksLayout = require('@khanacademy/wonder-blocks-layout');
|
|
29
29
|
var wonderBlocksDropdown = require('@khanacademy/wonder-blocks-dropdown');
|
|
30
30
|
var debounce = require('lodash.debounce');
|
|
31
|
+
var Pill = require('@khanacademy/wonder-blocks-pill');
|
|
32
|
+
var reactPopper = require('react-popper');
|
|
33
|
+
var Switch = require('@khanacademy/wonder-blocks-switch');
|
|
34
|
+
var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
|
|
31
35
|
var createReactClass = require('create-react-class');
|
|
32
36
|
var Link = require('@khanacademy/wonder-blocks-link');
|
|
33
37
|
var perseusCore = require('@khanacademy/perseus-core');
|
|
@@ -69,6 +73,8 @@ var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
|
|
|
69
73
|
var SimpleMarkdown__default = /*#__PURE__*/_interopDefaultLegacy(SimpleMarkdown);
|
|
70
74
|
var Button__default = /*#__PURE__*/_interopDefaultLegacy(Button);
|
|
71
75
|
var debounce__default = /*#__PURE__*/_interopDefaultLegacy(debounce);
|
|
76
|
+
var Pill__default = /*#__PURE__*/_interopDefaultLegacy(Pill);
|
|
77
|
+
var Switch__default = /*#__PURE__*/_interopDefaultLegacy(Switch);
|
|
72
78
|
var createReactClass__default = /*#__PURE__*/_interopDefaultLegacy(createReactClass);
|
|
73
79
|
var Link__default = /*#__PURE__*/_interopDefaultLegacy(Link);
|
|
74
80
|
|
|
@@ -268,7 +274,7 @@ class MathInput extends React__namespace.Component {
|
|
|
268
274
|
className = className + " " + this.props.className;
|
|
269
275
|
}
|
|
270
276
|
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
271
|
-
style: [styles$
|
|
277
|
+
style: [styles$C.outerWrapper, this.state.focused && styles$C.wrapperFocused, this.props.hasError && styles$C.wrapperError]
|
|
272
278
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
273
279
|
style: {
|
|
274
280
|
display: "flex",
|
|
@@ -295,7 +301,7 @@ class MathInput extends React__namespace.Component {
|
|
|
295
301
|
dismissEnabled: true,
|
|
296
302
|
content: () => /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.PopoverContentCore, {
|
|
297
303
|
closeButtonVisible: true,
|
|
298
|
-
style: styles$
|
|
304
|
+
style: styles$C.popoverContent
|
|
299
305
|
}, /*#__PURE__*/React__namespace.createElement(mathInput.DesktopKeypad, _extends({
|
|
300
306
|
onAnalyticsEvent: this.props.analytics.onAnalyticsEvent,
|
|
301
307
|
extraKeys: this.props.extraKeys,
|
|
@@ -335,9 +341,9 @@ const MathInputIcon = _ref => {
|
|
|
335
341
|
color = Color__default["default"].offBlack;
|
|
336
342
|
break;
|
|
337
343
|
}
|
|
338
|
-
const dynamicClass = active || focused ? styles$
|
|
344
|
+
const dynamicClass = active || focused ? styles$C.iconActive : styles$C.iconInactive;
|
|
339
345
|
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
340
|
-
style: [styles$
|
|
346
|
+
style: [styles$C.iconContainer, dynamicClass]
|
|
341
347
|
}, /*#__PURE__*/React__namespace.createElement("svg", {
|
|
342
348
|
xmlns: "http://www.w3.org/2000/svg",
|
|
343
349
|
width: "16",
|
|
@@ -382,7 +388,7 @@ const inputFocused = {
|
|
|
382
388
|
borderColor: Color__default["default"].blue,
|
|
383
389
|
margin: -1
|
|
384
390
|
};
|
|
385
|
-
const styles$
|
|
391
|
+
const styles$C = aphrodite.StyleSheet.create({
|
|
386
392
|
iconContainer: {
|
|
387
393
|
display: "flex",
|
|
388
394
|
justifyContent: "center",
|
|
@@ -17609,33 +17615,33 @@ class Lint extends React__namespace.Component {
|
|
|
17609
17615
|
let warningText;
|
|
17610
17616
|
let warningTextStyle;
|
|
17611
17617
|
if (this.props.severity === Severity.Error) {
|
|
17612
|
-
severityStyle = styles$
|
|
17618
|
+
severityStyle = styles$B.indicatorError;
|
|
17613
17619
|
warningText = "Error";
|
|
17614
|
-
warningTextStyle = styles$
|
|
17620
|
+
warningTextStyle = styles$B.publishBlockingError;
|
|
17615
17621
|
} else if (this.props.severity === Severity.Warning) {
|
|
17616
|
-
severityStyle = styles$
|
|
17622
|
+
severityStyle = styles$B.indicatorWarning;
|
|
17617
17623
|
warningText = "Warning";
|
|
17618
|
-
warningTextStyle = styles$
|
|
17624
|
+
warningTextStyle = styles$B.warning;
|
|
17619
17625
|
} else {
|
|
17620
|
-
severityStyle = styles$
|
|
17626
|
+
severityStyle = styles$B.indicatorGuideline;
|
|
17621
17627
|
warningText = "Recommendation";
|
|
17622
|
-
warningTextStyle = styles$
|
|
17628
|
+
warningTextStyle = styles$B.warning;
|
|
17623
17629
|
}
|
|
17624
17630
|
return /*#__PURE__*/React__namespace.createElement("a", {
|
|
17625
17631
|
href: `https://khanacademy.org/r/linter-rules#${this.props.ruleName}`,
|
|
17626
17632
|
target: "lint-help-window",
|
|
17627
17633
|
className: aphrodite.css(style)
|
|
17628
17634
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
17629
|
-
className: aphrodite.css(styles$
|
|
17635
|
+
className: aphrodite.css(styles$B.indicator, severityStyle)
|
|
17630
17636
|
}, this.props.severity === 1 && /*#__PURE__*/React__namespace.createElement(InlineIcon, exclamationIcon)), /*#__PURE__*/React__namespace.createElement("div", {
|
|
17631
|
-
className: aphrodite.css(styles$
|
|
17637
|
+
className: aphrodite.css(styles$B.tooltip, tooltipAbove && styles$B.tooltipAbove)
|
|
17632
17638
|
}, this.props.message.split("\n\n").map((m, i) => /*#__PURE__*/React__namespace.createElement("p", {
|
|
17633
17639
|
key: i,
|
|
17634
|
-
className: aphrodite.css(styles$
|
|
17640
|
+
className: aphrodite.css(styles$B.tooltipParagraph)
|
|
17635
17641
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
17636
17642
|
className: aphrodite.css(warningTextStyle)
|
|
17637
17643
|
}, warningText, ":", " "), m)), /*#__PURE__*/React__namespace.createElement("div", {
|
|
17638
|
-
className: aphrodite.css(styles$
|
|
17644
|
+
className: aphrodite.css(styles$B.tail, tooltipAbove && styles$B.tailAbove)
|
|
17639
17645
|
})));
|
|
17640
17646
|
};
|
|
17641
17647
|
|
|
@@ -17663,20 +17669,20 @@ class Lint extends React__namespace.Component {
|
|
|
17663
17669
|
}
|
|
17664
17670
|
if (blockHighlight) {
|
|
17665
17671
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
17666
|
-
className: aphrodite.css(styles$
|
|
17667
|
-
}, this.renderLink(styles$
|
|
17672
|
+
className: aphrodite.css(styles$B.lintContainer, styles$B.lintContainerBlock)
|
|
17673
|
+
}, this.renderLink(styles$B.radioWidgetHoverTarget), /*#__PURE__*/React__namespace.createElement("span", null, children));
|
|
17668
17674
|
}
|
|
17669
17675
|
if (inline) {
|
|
17670
17676
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
17671
|
-
className: aphrodite.css(styles$
|
|
17672
|
-
}, this.renderLink(styles$
|
|
17677
|
+
className: aphrodite.css(styles$B.lintContainer)
|
|
17678
|
+
}, this.renderLink(styles$B.inlineHoverTarget), /*#__PURE__*/React__namespace.createElement("span", null, children));
|
|
17673
17679
|
}
|
|
17674
17680
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
17675
|
-
className: aphrodite.css(styles$
|
|
17676
|
-
}, this.renderLink(styles$
|
|
17681
|
+
className: aphrodite.css(styles$B.lintContainer)
|
|
17682
|
+
}, this.renderLink(styles$B.hoverTarget), /*#__PURE__*/React__namespace.createElement("div", null, children));
|
|
17677
17683
|
}
|
|
17678
17684
|
}
|
|
17679
|
-
const styles$
|
|
17685
|
+
const styles$B = aphrodite.StyleSheet.create({
|
|
17680
17686
|
// This is the class of the outermost element.
|
|
17681
17687
|
// We use relative positioning so that the lint indicator can be
|
|
17682
17688
|
// positioned absolutely relative to the position of the linty container.
|
|
@@ -19724,7 +19730,7 @@ class Renderer extends React__namespace.Component {
|
|
|
19724
19730
|
};
|
|
19725
19731
|
const mobileInnerStyle = {
|
|
19726
19732
|
...innerStyle,
|
|
19727
|
-
...styles$
|
|
19733
|
+
...styles$A.mobileZoomableParentFix
|
|
19728
19734
|
};
|
|
19729
19735
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
19730
19736
|
key: state.key,
|
|
@@ -19861,7 +19867,7 @@ class Renderer extends React__namespace.Component {
|
|
|
19861
19867
|
};
|
|
19862
19868
|
const mobileInnerStyle = {
|
|
19863
19869
|
...innerStyle,
|
|
19864
|
-
...styles$
|
|
19870
|
+
...styles$A.mobileZoomableParentFix
|
|
19865
19871
|
};
|
|
19866
19872
|
const wrappedOutput = /*#__PURE__*/React__namespace.createElement("div", {
|
|
19867
19873
|
style: {
|
|
@@ -20367,7 +20373,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20367
20373
|
return this.lastRenderedMarkdown;
|
|
20368
20374
|
}
|
|
20369
20375
|
}
|
|
20370
|
-
const styles$
|
|
20376
|
+
const styles$A = {
|
|
20371
20377
|
mobileZoomableParentFix: {
|
|
20372
20378
|
// NOTE(abdul): There is an issue where transform animations will
|
|
20373
20379
|
// cause the Zoomable component to disappear when running on the
|
|
@@ -21302,10 +21308,10 @@ class InputNumber extends React__namespace.Component {
|
|
|
21302
21308
|
// Note: This is _very_ similar to what `numeric-input.jsx` does. If
|
|
21303
21309
|
// you modify this, double-check if you also need to modify that
|
|
21304
21310
|
// component.
|
|
21305
|
-
const inputStyles = [styles$
|
|
21311
|
+
const inputStyles = [styles$z.default, this.props.size === "small" ? styles$z.small : null, this.props.rightAlign ? styles$z.rightAlign : styles$z.leftAlign];
|
|
21306
21312
|
// Unanswered
|
|
21307
21313
|
if (rubric && !this.props.currentValue) {
|
|
21308
|
-
inputStyles.push(styles$
|
|
21314
|
+
inputStyles.push(styles$z.answerStateUnanswered);
|
|
21309
21315
|
}
|
|
21310
21316
|
return /*#__PURE__*/React__namespace.createElement(InputWithExamples
|
|
21311
21317
|
// eslint-disable-next-line react/no-string-refs
|
|
@@ -21453,7 +21459,7 @@ class InputNumber extends React__namespace.Component {
|
|
|
21453
21459
|
return answerString;
|
|
21454
21460
|
}
|
|
21455
21461
|
}
|
|
21456
|
-
const styles$
|
|
21462
|
+
const styles$z = aphrodite.StyleSheet.create({
|
|
21457
21463
|
default: {
|
|
21458
21464
|
width: 80,
|
|
21459
21465
|
height: "auto"
|
|
@@ -22451,7 +22457,7 @@ const FocusRing = function (props) {
|
|
|
22451
22457
|
};
|
|
22452
22458
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
22453
22459
|
"data-test-id": "focus-ring",
|
|
22454
|
-
className: aphrodite.css(styles$
|
|
22460
|
+
className: aphrodite.css(styles$y.ring),
|
|
22455
22461
|
style: style
|
|
22456
22462
|
}, children);
|
|
22457
22463
|
};
|
|
@@ -22460,7 +22466,7 @@ FocusRing.defaultProps = {
|
|
|
22460
22466
|
color: kaGreen$1,
|
|
22461
22467
|
multipleSelect: false
|
|
22462
22468
|
};
|
|
22463
|
-
const styles$
|
|
22469
|
+
const styles$y = aphrodite.StyleSheet.create({
|
|
22464
22470
|
ring: {
|
|
22465
22471
|
margin: "auto",
|
|
22466
22472
|
display: "inline-block",
|
|
@@ -22497,7 +22503,7 @@ function getChoiceLetter(pos) {
|
|
|
22497
22503
|
}
|
|
22498
22504
|
|
|
22499
22505
|
const CHOICE_ICON_SIZE = 24;
|
|
22500
|
-
const styles$
|
|
22506
|
+
const styles$x = aphrodite.StyleSheet.create({
|
|
22501
22507
|
iconWrapper: {
|
|
22502
22508
|
display: "inline-block",
|
|
22503
22509
|
position: "relative"
|
|
@@ -22520,7 +22526,7 @@ const CrossOutLine = function (props) {
|
|
|
22520
22526
|
width: CROSS_OUT_LINE_SIZE,
|
|
22521
22527
|
height: CROSS_OUT_LINE_SIZE,
|
|
22522
22528
|
viewBox: `0 0 ${CROSS_OUT_LINE_SIZE} ${CROSS_OUT_LINE_SIZE}`,
|
|
22523
|
-
className: aphrodite.css(styles$
|
|
22529
|
+
className: aphrodite.css(styles$w.crossOutLine),
|
|
22524
22530
|
"data-test-id": "choice-icon__cross-out-line"
|
|
22525
22531
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
22526
22532
|
// The line stretches from the bottom-left to top-right.
|
|
@@ -22535,7 +22541,7 @@ const CrossOutLine = function (props) {
|
|
|
22535
22541
|
strokeLinecap: "round"
|
|
22536
22542
|
}));
|
|
22537
22543
|
};
|
|
22538
|
-
const styles$
|
|
22544
|
+
const styles$w = aphrodite.StyleSheet.create({
|
|
22539
22545
|
crossOutLine: {
|
|
22540
22546
|
// Center the icon within the container.
|
|
22541
22547
|
position: "absolute",
|
|
@@ -22616,7 +22622,7 @@ const ChoiceIcon = function (props) {
|
|
|
22616
22622
|
} = props;
|
|
22617
22623
|
const dynamicStyles = getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct);
|
|
22618
22624
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
22619
|
-
className: aphrodite.css(styles$
|
|
22625
|
+
className: aphrodite.css(styles$x.iconWrapper)
|
|
22620
22626
|
}, /*#__PURE__*/React__namespace.createElement(FocusRing, {
|
|
22621
22627
|
color: Color__default["default"].blue,
|
|
22622
22628
|
visible: focused || hovered,
|
|
@@ -22625,13 +22631,13 @@ const ChoiceIcon = function (props) {
|
|
|
22625
22631
|
// @ts-expect-error - TS2322 - Type '{ backgroundColor: string | null | undefined; borderColor: string; color: string; borderRadius: number; }' is not assignable to type 'Properties<string | number, string & {}>'.
|
|
22626
22632
|
style: dynamicStyles,
|
|
22627
22633
|
"data-test-id": "choice-icon__library-choice-icon",
|
|
22628
|
-
className: aphrodite.css(styles$
|
|
22634
|
+
className: aphrodite.css(styles$v.circle, showCorrectness && correct && styles$v.circleCorrect, showCorrectness && !correct && styles$v.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$v.circleIncorrectAnswered)
|
|
22629
22635
|
// used in BaseRadio editMode to check
|
|
22630
22636
|
// if we actually clicked on the radio icon
|
|
22631
22637
|
,
|
|
22632
22638
|
"data-is-radio-icon": true
|
|
22633
22639
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
22634
|
-
className: aphrodite.css(styles$
|
|
22640
|
+
className: aphrodite.css(styles$v.innerWrapper)
|
|
22635
22641
|
}, /*#__PURE__*/React__namespace.createElement(ChoiceInner, {
|
|
22636
22642
|
pos: pos,
|
|
22637
22643
|
showCorrectness: showCorrectness,
|
|
@@ -22640,7 +22646,7 @@ const ChoiceIcon = function (props) {
|
|
|
22640
22646
|
color: dynamicStyles.borderColor
|
|
22641
22647
|
}));
|
|
22642
22648
|
};
|
|
22643
|
-
const styles$
|
|
22649
|
+
const styles$v = aphrodite.StyleSheet.create({
|
|
22644
22650
|
innerWrapper: {
|
|
22645
22651
|
width: CHOICE_ICON_SIZE,
|
|
22646
22652
|
height: CHOICE_ICON_SIZE,
|
|
@@ -22732,19 +22738,19 @@ const OptionStatus = function (props) {
|
|
|
22732
22738
|
}
|
|
22733
22739
|
let textStyle;
|
|
22734
22740
|
if (correct) {
|
|
22735
|
-
textStyle = styles$
|
|
22741
|
+
textStyle = styles$u.correct;
|
|
22736
22742
|
} else {
|
|
22737
22743
|
if (checked || previouslyAnswered) {
|
|
22738
|
-
textStyle = styles$
|
|
22744
|
+
textStyle = styles$u.incorrectAnswered;
|
|
22739
22745
|
} else {
|
|
22740
|
-
textStyle = styles$
|
|
22746
|
+
textStyle = styles$u.incorrect;
|
|
22741
22747
|
}
|
|
22742
22748
|
}
|
|
22743
22749
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
22744
|
-
className: aphrodite.css(styles$
|
|
22750
|
+
className: aphrodite.css(styles$u.text, textStyle)
|
|
22745
22751
|
}, renderText(checked, correct, crossedOut));
|
|
22746
22752
|
};
|
|
22747
|
-
const styles$
|
|
22753
|
+
const styles$u = aphrodite.StyleSheet.create({
|
|
22748
22754
|
text: {
|
|
22749
22755
|
alignItems: "center",
|
|
22750
22756
|
display: "flex",
|
|
@@ -22818,8 +22824,8 @@ const Choice = function (props) {
|
|
|
22818
22824
|
crossedOut: updatedCrossedOut
|
|
22819
22825
|
});
|
|
22820
22826
|
}
|
|
22821
|
-
const descriptionClassName = classNames__default["default"]("description", aphrodite.css(styles$
|
|
22822
|
-
const rationaleClassName = classNames__default["default"]("perseus-radio-rationale-content", aphrodite.css(styles$
|
|
22827
|
+
const descriptionClassName = classNames__default["default"]("description", aphrodite.css(styles$t.description));
|
|
22828
|
+
const rationaleClassName = classNames__default["default"]("perseus-radio-rationale-content", aphrodite.css(styles$t.rationale));
|
|
22823
22829
|
|
|
22824
22830
|
// We want to show the choices as dimmed out when the choices are disabled.
|
|
22825
22831
|
// However, we don't want to do this when we're in review mode in the
|
|
@@ -22986,7 +22992,7 @@ const Choice = function (props) {
|
|
|
22986
22992
|
"data-test-id": `perseus-radio-rationale-content-${pos}`
|
|
22987
22993
|
}, rationale));
|
|
22988
22994
|
};
|
|
22989
|
-
const styles$
|
|
22995
|
+
const styles$t = aphrodite.StyleSheet.create({
|
|
22990
22996
|
description: {
|
|
22991
22997
|
display: "inline-block",
|
|
22992
22998
|
width: "100%"
|
|
@@ -23178,10 +23184,10 @@ const BaseRadio = function (props) {
|
|
|
23178
23184
|
const isMobile = apiOptions.isMobile;
|
|
23179
23185
|
const firstChoiceHighlighted = choices[0].highlighted;
|
|
23180
23186
|
const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
|
|
23181
|
-
const className = classNames__default["default"]("perseus-widget-radio", !editMode && "perseus-rendered-radio", aphrodite.css(styles$
|
|
23182
|
-
const instructionsClassName = classNames__default["default"]("instructions", aphrodite.css(styles$
|
|
23187
|
+
const className = classNames__default["default"]("perseus-widget-radio", !editMode && "perseus-rendered-radio", aphrodite.css(styles$s.radio, styles$s.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$s.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$s.radioContainerLastHighlighted));
|
|
23188
|
+
const instructionsClassName = classNames__default["default"]("instructions", aphrodite.css(styles$s.instructions, isMobile && styles$s.instructionsMobile));
|
|
23183
23189
|
const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect);
|
|
23184
|
-
const responsiveClassName = aphrodite.css(styles$
|
|
23190
|
+
const responsiveClassName = aphrodite.css(styles$s.responsiveFieldset);
|
|
23185
23191
|
const fieldset = /*#__PURE__*/React__namespace.createElement("fieldset", {
|
|
23186
23192
|
className: `perseus-widget-radio-fieldset ${responsiveClassName}`
|
|
23187
23193
|
}, /*#__PURE__*/React__namespace.createElement("legend", {
|
|
@@ -23232,7 +23238,7 @@ const BaseRadio = function (props) {
|
|
|
23232
23238
|
const aphroditeClassName = checked => {
|
|
23233
23239
|
// Whether or not to show correctness borders
|
|
23234
23240
|
// for this choice and the next choice.
|
|
23235
|
-
return aphrodite.css(sharedStyles.aboveScratchpad, styles$
|
|
23241
|
+
return aphrodite.css(sharedStyles.aboveScratchpad, styles$s.item, styles$s.responsiveItem, checked && styles$s.selectedItem, checked && choice.highlighted && styles$s.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$s.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$s.nextHighlighted);
|
|
23236
23242
|
};
|
|
23237
23243
|
|
|
23238
23244
|
// HACK(abdulrahman): Preloads the selection-state
|
|
@@ -23306,14 +23312,14 @@ const BaseRadio = function (props) {
|
|
|
23306
23312
|
// Allow for horizontal scrolling if content is too wide, which may be
|
|
23307
23313
|
// an issue especially on phones.
|
|
23308
23314
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
23309
|
-
className: aphrodite.css(styles$
|
|
23315
|
+
className: aphrodite.css(styles$s.responsiveContainer)
|
|
23310
23316
|
}, fieldset);
|
|
23311
23317
|
};
|
|
23312
23318
|
BaseRadio.defaultProps = {
|
|
23313
23319
|
editMode: false,
|
|
23314
23320
|
multipleSelect: false
|
|
23315
23321
|
};
|
|
23316
|
-
const styles$
|
|
23322
|
+
const styles$s = aphrodite.StyleSheet.create({
|
|
23317
23323
|
instructions: {
|
|
23318
23324
|
display: "block",
|
|
23319
23325
|
color: gray17,
|
|
@@ -23972,9 +23978,9 @@ class Categorizer extends React__namespace.Component {
|
|
|
23972
23978
|
indexedItems = Util.shuffle(indexedItems, this.props.problemNum);
|
|
23973
23979
|
}
|
|
23974
23980
|
const table = /*#__PURE__*/React__namespace.createElement("table", {
|
|
23975
|
-
className: "categorizer-table " + aphrodite.css(styles$
|
|
23981
|
+
className: "categorizer-table " + aphrodite.css(styles$r.mobileTable)
|
|
23976
23982
|
}, /*#__PURE__*/React__namespace.createElement("thead", null, /*#__PURE__*/React__namespace.createElement("tr", null, /*#__PURE__*/React__namespace.createElement("td", {
|
|
23977
|
-
className: aphrodite.css(styles$
|
|
23983
|
+
className: aphrodite.css(styles$r.emptyHeaderCell)
|
|
23978
23984
|
}), this.props.categories.map((category, i) => {
|
|
23979
23985
|
// Array index is the correct key here, as that's
|
|
23980
23986
|
// how category grading actually works -- no way
|
|
@@ -23982,7 +23988,7 @@ class Categorizer extends React__namespace.Component {
|
|
|
23982
23988
|
// middle. (If we later add that, this should be
|
|
23983
23989
|
// fixed.)
|
|
23984
23990
|
return /*#__PURE__*/React__namespace.createElement("th", {
|
|
23985
|
-
className: aphrodite.css(styles$
|
|
23991
|
+
className: aphrodite.css(styles$r.header),
|
|
23986
23992
|
key: i
|
|
23987
23993
|
}, /*#__PURE__*/React__namespace.createElement(Renderer, {
|
|
23988
23994
|
content: category,
|
|
@@ -24002,7 +24008,7 @@ class Categorizer extends React__namespace.Component {
|
|
|
24002
24008
|
})), self.props.categories.map((catName, catNum) => {
|
|
24003
24009
|
const selected = self.props.values[itemNum] === catNum;
|
|
24004
24010
|
return /*#__PURE__*/React__namespace.createElement("td", {
|
|
24005
|
-
className: "category " + aphrodite.css(styles$
|
|
24011
|
+
className: "category " + aphrodite.css(styles$r.cell),
|
|
24006
24012
|
key: catNum
|
|
24007
24013
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
24008
24014
|
className: ClassNames.INTERACTIVE,
|
|
@@ -24021,7 +24027,7 @@ class Categorizer extends React__namespace.Component {
|
|
|
24021
24027
|
onChange: this.onChange.bind(this, itemNum, catNum),
|
|
24022
24028
|
onClick: e => e.stopPropagation()
|
|
24023
24029
|
}), !isMobile && /*#__PURE__*/React__namespace.createElement("span", {
|
|
24024
|
-
className: aphrodite.css(styles$
|
|
24030
|
+
className: aphrodite.css(styles$r.radioSpan, selected && styles$r.checkedRadioSpan, this.props.static && selected && styles$r.staticCheckedRadioSpan)
|
|
24025
24031
|
}, selected ? /*#__PURE__*/React__namespace.createElement(InlineIcon, iconCircle) : /*#__PURE__*/React__namespace.createElement(InlineIcon, iconCircleThin))));
|
|
24026
24032
|
}));
|
|
24027
24033
|
/* eslint-enable max-len */
|
|
@@ -24033,7 +24039,7 @@ class Categorizer extends React__namespace.Component {
|
|
|
24033
24039
|
"categorizer-container": true,
|
|
24034
24040
|
"static-mode": this.props.static
|
|
24035
24041
|
});
|
|
24036
|
-
const inlineStyles = this.props.apiOptions.isMobile ? [styles$
|
|
24042
|
+
const inlineStyles = this.props.apiOptions.isMobile ? [styles$r.fullBleedContainer] : [];
|
|
24037
24043
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24038
24044
|
className: extraClassNames + " " + aphrodite.css(...inlineStyles)
|
|
24039
24045
|
}, table);
|
|
@@ -24082,7 +24088,7 @@ const pageMargin = 16;
|
|
|
24082
24088
|
|
|
24083
24089
|
// Stylesheets aren't directly testable
|
|
24084
24090
|
/* c8 ignore next */
|
|
24085
|
-
const styles$
|
|
24091
|
+
const styles$r = aphrodite.StyleSheet.create({
|
|
24086
24092
|
mobileTable: {
|
|
24087
24093
|
[mediaQueries.smOrSmaller]: {
|
|
24088
24094
|
minWidth: "auto"
|
|
@@ -24345,7 +24351,7 @@ class CSProgram extends React__namespace.Component {
|
|
|
24345
24351
|
// creator "went wild".
|
|
24346
24352
|
// http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
|
|
24347
24353
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24348
|
-
className: aphrodite.css(styles$
|
|
24354
|
+
className: aphrodite.css(styles$q.widthOverride, styleContainer && styles$q.container)
|
|
24349
24355
|
}, /*#__PURE__*/React__namespace.createElement("iframe", {
|
|
24350
24356
|
sandbox: sandboxOptions,
|
|
24351
24357
|
src: url,
|
|
@@ -24355,7 +24361,7 @@ class CSProgram extends React__namespace.Component {
|
|
|
24355
24361
|
}));
|
|
24356
24362
|
}
|
|
24357
24363
|
}
|
|
24358
|
-
const styles$
|
|
24364
|
+
const styles$q = aphrodite.StyleSheet.create({
|
|
24359
24365
|
// Override the inherited width from the perseus paragraph class
|
|
24360
24366
|
// 820 is minimum width for 420px editor and 400px canvas
|
|
24361
24367
|
widthOverride: {
|
|
@@ -24403,7 +24409,7 @@ class Definition extends React__namespace.Component {
|
|
|
24403
24409
|
return /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.Popover, {
|
|
24404
24410
|
content: /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.PopoverContentCore, {
|
|
24405
24411
|
color: "white",
|
|
24406
|
-
style: styles$
|
|
24412
|
+
style: styles$p.tooltipBody,
|
|
24407
24413
|
closeButtonVisible: true
|
|
24408
24414
|
}, /*#__PURE__*/React__namespace.createElement(Renderer, {
|
|
24409
24415
|
apiOptions: this.props.apiOptions,
|
|
@@ -24434,7 +24440,7 @@ class Definition extends React__namespace.Component {
|
|
|
24434
24440
|
});
|
|
24435
24441
|
}
|
|
24436
24442
|
}
|
|
24437
|
-
const styles$
|
|
24443
|
+
const styles$p = {
|
|
24438
24444
|
tooltipBody: {
|
|
24439
24445
|
color: Color__default["default"].offBlack,
|
|
24440
24446
|
fontSize: 20,
|
|
@@ -24599,15 +24605,15 @@ class Explanation extends React__namespace.Component {
|
|
|
24599
24605
|
const onClick = this._onClick;
|
|
24600
24606
|
if (isMobile) {
|
|
24601
24607
|
linkContainer = /*#__PURE__*/React__namespace.createElement("div", {
|
|
24602
|
-
className: aphrodite.css(styles$
|
|
24608
|
+
className: aphrodite.css(styles$o.linkContainer)
|
|
24603
24609
|
}, /*#__PURE__*/React__namespace.createElement("a", {
|
|
24604
|
-
className: aphrodite.css(styles$
|
|
24610
|
+
className: aphrodite.css(styles$o.mobileExplanationLink),
|
|
24605
24611
|
href: href,
|
|
24606
24612
|
onClick: onClick,
|
|
24607
24613
|
role: "button",
|
|
24608
24614
|
"aria-expanded": this.state.expanded
|
|
24609
24615
|
}, linkAnchor), this.state.expanded && /*#__PURE__*/React__namespace.createElement("svg", {
|
|
24610
|
-
className: aphrodite.css(styles$
|
|
24616
|
+
className: aphrodite.css(styles$o.disclosureArrow)
|
|
24611
24617
|
}, /*#__PURE__*/React__namespace.createElement("polygon", {
|
|
24612
24618
|
style: {
|
|
24613
24619
|
fill: backgroundColor
|
|
@@ -24620,13 +24626,13 @@ class Explanation extends React__namespace.Component {
|
|
|
24620
24626
|
// an anchor tag (if rendered in a modal) or a Wonder Blocks
|
|
24621
24627
|
// link component in lesson page, see: `article-page.jsx`.
|
|
24622
24628
|
linkContainer = /*#__PURE__*/React__namespace.createElement("div", {
|
|
24623
|
-
className: aphrodite.css(styles$
|
|
24629
|
+
className: aphrodite.css(styles$o.linkContainer, styles$o.articleLink)
|
|
24624
24630
|
}, /*#__PURE__*/React__namespace.createElement(Link, {
|
|
24625
|
-
className: aphrodite.css(styles$
|
|
24631
|
+
className: aphrodite.css(styles$o.explanationLink)
|
|
24626
24632
|
// HACK(michaelpolyak): WB Link doesn't passthrough
|
|
24627
24633
|
// class name.
|
|
24628
24634
|
,
|
|
24629
|
-
style: styles$
|
|
24635
|
+
style: styles$o.explanationLink,
|
|
24630
24636
|
href: href,
|
|
24631
24637
|
onClick: onClick,
|
|
24632
24638
|
role: "button",
|
|
@@ -24636,9 +24642,9 @@ class Explanation extends React__namespace.Component {
|
|
|
24636
24642
|
// NOTE: For exercises, the baseElements `Link` is an
|
|
24637
24643
|
// anchor tag, see: `perseus-api.jsx`.
|
|
24638
24644
|
linkContainer = /*#__PURE__*/React__namespace.createElement("div", {
|
|
24639
|
-
className: aphrodite.css(styles$
|
|
24645
|
+
className: aphrodite.css(styles$o.linkContainer, styles$o.exerciseLink)
|
|
24640
24646
|
}, /*#__PURE__*/React__namespace.createElement(Link, {
|
|
24641
|
-
className: aphrodite.css(styles$
|
|
24647
|
+
className: aphrodite.css(styles$o.explanationLink),
|
|
24642
24648
|
href: href,
|
|
24643
24649
|
onClick: onClick,
|
|
24644
24650
|
role: "button",
|
|
@@ -24646,11 +24652,11 @@ class Explanation extends React__namespace.Component {
|
|
|
24646
24652
|
}, `[${linkAnchor}]`));
|
|
24647
24653
|
}
|
|
24648
24654
|
}
|
|
24649
|
-
const expandedStyle = isMobile ? styles$
|
|
24655
|
+
const expandedStyle = isMobile ? styles$o.contentExpandedMobile : styles$o.contentExpanded;
|
|
24650
24656
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24651
|
-
className: aphrodite.css(styles$
|
|
24657
|
+
className: aphrodite.css(styles$o.container)
|
|
24652
24658
|
}, linkContainer, this.state.expanded && /*#__PURE__*/React__namespace.createElement("div", {
|
|
24653
|
-
className: aphrodite.css(styles$
|
|
24659
|
+
className: aphrodite.css(styles$o.content, isMobile && styles$o.contentMobile, this.state.expanded && expandedStyle)
|
|
24654
24660
|
// eslint-disable-next-line react/no-string-refs
|
|
24655
24661
|
,
|
|
24656
24662
|
ref: "content"
|
|
@@ -24673,7 +24679,7 @@ const verticalContentPadding = 10;
|
|
|
24673
24679
|
const arrowWidth = 30;
|
|
24674
24680
|
const arrowHeight = 14;
|
|
24675
24681
|
const backgroundColor = gray95;
|
|
24676
|
-
const styles$
|
|
24682
|
+
const styles$o = aphrodite.StyleSheet.create({
|
|
24677
24683
|
container: {
|
|
24678
24684
|
display: "inline",
|
|
24679
24685
|
position: "relative"
|
|
@@ -24779,15 +24785,15 @@ class GradedGroupAnswerBar extends React__namespace.Component {
|
|
|
24779
24785
|
onNextQuestion
|
|
24780
24786
|
} = this.props;
|
|
24781
24787
|
const answerBarStyle = {
|
|
24782
|
-
...styles$
|
|
24788
|
+
...styles$n.answerBar,
|
|
24783
24789
|
backgroundColor: answerBarState === "CORRECT" ? Color__default["default"].offWhite : "white",
|
|
24784
24790
|
// Center the "Correct!" message only when there's no next question
|
|
24785
24791
|
justifyContent: answerBarState === "CORRECT" && !onNextQuestion ? "center" : "space-between"
|
|
24786
24792
|
};
|
|
24787
24793
|
const message = answerBarState === "INCORRECT" ? /*#__PURE__*/React__namespace.createElement("span", {
|
|
24788
|
-
style: styles$
|
|
24794
|
+
style: styles$n.text
|
|
24789
24795
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
24790
|
-
style: styles$
|
|
24796
|
+
style: styles$n.tryAgainIcon
|
|
24791
24797
|
}, /*#__PURE__*/React__namespace.createElement(InlineIcon, iconTryAgain)), /*#__PURE__*/React__namespace.createElement("span", {
|
|
24792
24798
|
style: {
|
|
24793
24799
|
marginLeft: 8
|
|
@@ -24806,7 +24812,7 @@ class GradedGroupAnswerBar extends React__namespace.Component {
|
|
|
24806
24812
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
24807
24813
|
style: answerBarStyle
|
|
24808
24814
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
24809
|
-
style: styles$
|
|
24815
|
+
style: styles$n.text
|
|
24810
24816
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
24811
24817
|
style: {
|
|
24812
24818
|
fontSize: 28,
|
|
@@ -24828,7 +24834,7 @@ class GradedGroupAnswerBar extends React__namespace.Component {
|
|
|
24828
24834
|
}
|
|
24829
24835
|
}
|
|
24830
24836
|
const fontSize = 17;
|
|
24831
|
-
const styles$
|
|
24837
|
+
const styles$n = {
|
|
24832
24838
|
answerBar: {
|
|
24833
24839
|
display: "flex",
|
|
24834
24840
|
alignItems: "center",
|
|
@@ -25028,7 +25034,7 @@ class GradedGroup extends React__namespace.Component {
|
|
|
25028
25034
|
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
|
|
25029
25035
|
gradeStatus = i18n__namespace._("Incorrect");
|
|
25030
25036
|
}
|
|
25031
|
-
const mobileClass = this.props.inGradedGroupSet ? aphrodite.css(styles$
|
|
25037
|
+
const mobileClass = this.props.inGradedGroupSet ? aphrodite.css(styles$m.gradedGroupInSet) : aphrodite.css(styles$m.gradedGroup);
|
|
25032
25038
|
const classes = classNames__default["default"]({
|
|
25033
25039
|
[mobileClass]: apiOptions.isMobile,
|
|
25034
25040
|
"perseus-graded-group": true,
|
|
@@ -25047,7 +25053,7 @@ class GradedGroup extends React__namespace.Component {
|
|
|
25047
25053
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
25048
25054
|
className: classes
|
|
25049
25055
|
}, !!this.props.title && /*#__PURE__*/React__namespace.createElement("div", {
|
|
25050
|
-
className: aphrodite.css(styles$
|
|
25056
|
+
className: aphrodite.css(styles$m.title)
|
|
25051
25057
|
}, this.props.title), /*#__PURE__*/React__namespace.createElement(Renderer, _extends({}, this.props, {
|
|
25052
25058
|
// eslint-disable-next-line react/no-string-refs
|
|
25053
25059
|
ref: "renderer",
|
|
@@ -25080,7 +25086,7 @@ class GradedGroup extends React__namespace.Component {
|
|
|
25080
25086
|
}, i18n__namespace._("Next question")), this.props.hint && this.props.hint.content && (this.state.showHint ? /*#__PURE__*/React__namespace.createElement("div", null, /*#__PURE__*/React__namespace.createElement("button", {
|
|
25081
25087
|
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
|
|
25082
25088
|
tabIndex: "0",
|
|
25083
|
-
className: aphrodite.css(styles$
|
|
25089
|
+
className: aphrodite.css(styles$m.explanationTitle),
|
|
25084
25090
|
onClick: () => this.setState({
|
|
25085
25091
|
showHint: false
|
|
25086
25092
|
}),
|
|
@@ -25112,7 +25118,7 @@ class GradedGroup extends React__namespace.Component {
|
|
|
25112
25118
|
showHint: true
|
|
25113
25119
|
});
|
|
25114
25120
|
},
|
|
25115
|
-
className: aphrodite.css(styles$
|
|
25121
|
+
className: aphrodite.css(styles$m.showHintLink)
|
|
25116
25122
|
}, i18n__namespace._("Explain"))), apiOptions.isMobile && answerBarState !== "HIDDEN" && /*#__PURE__*/React__namespace.createElement(GradedGroupAnswerBar, {
|
|
25117
25123
|
apiOptions: apiOptions,
|
|
25118
25124
|
answerBarState: answerBarState,
|
|
@@ -25124,7 +25130,7 @@ class GradedGroup extends React__namespace.Component {
|
|
|
25124
25130
|
const traverseChildWidgets$3 = function (props, traverseRenderer) {
|
|
25125
25131
|
return ___default["default"].extend({}, props, traverseRenderer(props));
|
|
25126
25132
|
};
|
|
25127
|
-
const styles$
|
|
25133
|
+
const styles$m = aphrodite.StyleSheet.create({
|
|
25128
25134
|
gradedGroupInSet: {
|
|
25129
25135
|
// Reset a few desktop-only styles that come from graded-group.less
|
|
25130
25136
|
marginLeft: 0,
|
|
@@ -25194,20 +25200,20 @@ class Indicators extends React__namespace.Component {
|
|
|
25194
25200
|
return /*#__PURE__*/React__namespace.createElement("ul", {
|
|
25195
25201
|
// reduntantly add class name for use in .less files--
|
|
25196
25202
|
// the styles object key gets hashed
|
|
25197
|
-
className: classNames__default["default"](aphrodite.css(styles$
|
|
25203
|
+
className: classNames__default["default"](aphrodite.css(styles$l.indicatorContainer), "indicatorContainer")
|
|
25198
25204
|
}, this.props.gradedGroups.map((_ref, i) => {
|
|
25199
25205
|
let {
|
|
25200
25206
|
title
|
|
25201
25207
|
} = _ref;
|
|
25202
25208
|
return /*#__PURE__*/React__namespace.createElement("li", {
|
|
25203
|
-
className: aphrodite.css(styles$
|
|
25209
|
+
className: aphrodite.css(styles$l.indicator),
|
|
25204
25210
|
key: title
|
|
25205
25211
|
}, /*#__PURE__*/React__namespace.createElement(Clickable__default["default"], {
|
|
25206
25212
|
role: "button",
|
|
25207
25213
|
"aria-label": i18n__namespace._("Skip to %(title)s", {
|
|
25208
25214
|
title
|
|
25209
25215
|
}),
|
|
25210
|
-
style: styles$
|
|
25216
|
+
style: styles$l.indicatorButton,
|
|
25211
25217
|
onClick: () => this.props.onChangeCurrentGroup(i),
|
|
25212
25218
|
onKeyDown: e => this.handleKeyDown(e, i)
|
|
25213
25219
|
}, _ref2 => {
|
|
@@ -25217,9 +25223,9 @@ class Indicators extends React__namespace.Component {
|
|
|
25217
25223
|
pressed
|
|
25218
25224
|
} = _ref2;
|
|
25219
25225
|
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
25220
|
-
style: [styles$
|
|
25226
|
+
style: [styles$l.indicatorDot, (hovered || focused || pressed) && styles$l.indicatorDotFocused]
|
|
25221
25227
|
}, i === this.props.currentGroup && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
25222
|
-
style: styles$
|
|
25228
|
+
style: styles$l.indicatorDotActive
|
|
25223
25229
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
25224
25230
|
className: aphrodite.css(a11y.srOnly)
|
|
25225
25231
|
}, i18n__namespace._("Current"))));
|
|
@@ -25295,7 +25301,7 @@ class GradedGroupSet extends React__namespace.Component {
|
|
|
25295
25301
|
} = getDependencies();
|
|
25296
25302
|
if (JIPT.useJIPT && this.props.gradedGroups.length > 1) {
|
|
25297
25303
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
25298
|
-
className: aphrodite.css(styles$
|
|
25304
|
+
className: aphrodite.css(styles$l.container)
|
|
25299
25305
|
}, this.props.gradedGroups.map((gradedGroup, i) => {
|
|
25300
25306
|
return (
|
|
25301
25307
|
/*#__PURE__*/
|
|
@@ -25318,13 +25324,13 @@ class GradedGroupSet extends React__namespace.Component {
|
|
|
25318
25324
|
const numGroups = this.props.gradedGroups.length;
|
|
25319
25325
|
const handleNextQuestion = this.state.currentGroup < numGroups - 1 ? this.handleNextQuestion : null;
|
|
25320
25326
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
25321
|
-
className: aphrodite.css(styles$
|
|
25327
|
+
className: aphrodite.css(styles$l.container)
|
|
25322
25328
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
25323
|
-
className: aphrodite.css(styles$
|
|
25329
|
+
className: aphrodite.css(styles$l.top)
|
|
25324
25330
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
25325
|
-
className: aphrodite.css(styles$
|
|
25331
|
+
className: aphrodite.css(styles$l.title)
|
|
25326
25332
|
}, currentGroup.title), /*#__PURE__*/React__namespace.createElement("div", {
|
|
25327
|
-
className: aphrodite.css(styles$
|
|
25333
|
+
className: aphrodite.css(styles$l.spacer)
|
|
25328
25334
|
}), /*#__PURE__*/React__namespace.createElement(Indicators, {
|
|
25329
25335
|
currentGroup: this.state.currentGroup,
|
|
25330
25336
|
gradedGroups: this.props.gradedGroups,
|
|
@@ -25362,7 +25368,7 @@ var GradedGroupSet$1 = {
|
|
|
25362
25368
|
tracking: "all",
|
|
25363
25369
|
isLintable: true
|
|
25364
25370
|
};
|
|
25365
|
-
const styles$
|
|
25371
|
+
const styles$l = aphrodite.StyleSheet.create({
|
|
25366
25372
|
top: {
|
|
25367
25373
|
display: "flex",
|
|
25368
25374
|
flexDirection: "row"
|
|
@@ -25467,7 +25473,7 @@ class ButtonGroup extends React__namespace.Component {
|
|
|
25467
25473
|
id: "" + i,
|
|
25468
25474
|
ref: "button" + i,
|
|
25469
25475
|
key: "" + i,
|
|
25470
|
-
className: aphrodite.css(styles$
|
|
25476
|
+
className: aphrodite.css(styles$k.buttonStyle, button.value === value && styles$k.selectedStyle),
|
|
25471
25477
|
onClick: () => this.toggleSelect(button.value)
|
|
25472
25478
|
}, button.content || "" + button.value);
|
|
25473
25479
|
});
|
|
@@ -25480,7 +25486,7 @@ class ButtonGroup extends React__namespace.Component {
|
|
|
25480
25486
|
}, buttons);
|
|
25481
25487
|
}
|
|
25482
25488
|
}
|
|
25483
|
-
const styles$
|
|
25489
|
+
const styles$k = aphrodite.StyleSheet.create({
|
|
25484
25490
|
buttonStyle: {
|
|
25485
25491
|
backgroundColor: "white",
|
|
25486
25492
|
border: "1px solid #ccc",
|
|
@@ -31008,7 +31014,7 @@ class AnswerChoice extends React__namespace.Component {
|
|
|
31008
31014
|
checked
|
|
31009
31015
|
} = this.props;
|
|
31010
31016
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
31011
|
-
className: aphrodite.css(styles$
|
|
31017
|
+
className: aphrodite.css(styles$j.checkmarkContainer)
|
|
31012
31018
|
}, checked && /*#__PURE__*/React__namespace.createElement(Icon, {
|
|
31013
31019
|
icon: iconCheckMedium,
|
|
31014
31020
|
size: 16,
|
|
@@ -31028,11 +31034,11 @@ class AnswerChoice extends React__namespace.Component {
|
|
|
31028
31034
|
} = this.state;
|
|
31029
31035
|
const answerId = `${groupName}-${index}`;
|
|
31030
31036
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
31031
|
-
className: aphrodite.css(styles$
|
|
31037
|
+
className: aphrodite.css(styles$j.answerChoice, isInputFocused && styles$j.answerFocused),
|
|
31032
31038
|
onClick: e => this.handleClick(e),
|
|
31033
31039
|
onKeyDown: e => this.handleKeyDown(e)
|
|
31034
31040
|
}, /*#__PURE__*/React__namespace.createElement("span", {
|
|
31035
|
-
className: aphrodite.css(styles$
|
|
31041
|
+
className: aphrodite.css(styles$j.answerInputWrapper)
|
|
31036
31042
|
}, /*#__PURE__*/React__namespace.createElement("input", {
|
|
31037
31043
|
className: aphrodite.css(sharedStyles.perseusSrOnly),
|
|
31038
31044
|
checked: checked,
|
|
@@ -31101,7 +31107,13 @@ class AnswerChoices extends React__namespace.Component {
|
|
|
31101
31107
|
const {
|
|
31102
31108
|
groupName
|
|
31103
31109
|
} = this.state;
|
|
31104
|
-
return /*#__PURE__*/React__namespace.createElement("fieldset",
|
|
31110
|
+
return /*#__PURE__*/React__namespace.createElement("fieldset", {
|
|
31111
|
+
style: {
|
|
31112
|
+
border: "none",
|
|
31113
|
+
margin: 0,
|
|
31114
|
+
padding: 0
|
|
31115
|
+
}
|
|
31116
|
+
}, choices.map((choice, index) => /*#__PURE__*/React__namespace.createElement(AnswerChoice, _extends({}, choice, {
|
|
31105
31117
|
key: choice.content,
|
|
31106
31118
|
groupName: groupName,
|
|
31107
31119
|
index: index,
|
|
@@ -31113,7 +31125,7 @@ class AnswerChoices extends React__namespace.Component {
|
|
|
31113
31125
|
}))));
|
|
31114
31126
|
}
|
|
31115
31127
|
}
|
|
31116
|
-
const styles$
|
|
31128
|
+
const styles$j = aphrodite.StyleSheet.create({
|
|
31117
31129
|
answerChoice: {
|
|
31118
31130
|
display: "flex",
|
|
31119
31131
|
alignItems: "center",
|
|
@@ -31144,44 +31156,125 @@ const styles$h = aphrodite.StyleSheet.create({
|
|
|
31144
31156
|
}
|
|
31145
31157
|
});
|
|
31146
31158
|
|
|
31159
|
+
const AnswerPill = props => {
|
|
31160
|
+
const {
|
|
31161
|
+
selectedAnswers,
|
|
31162
|
+
showCorrectness,
|
|
31163
|
+
markerRef,
|
|
31164
|
+
side,
|
|
31165
|
+
onClick,
|
|
31166
|
+
style
|
|
31167
|
+
} = props;
|
|
31168
|
+
const idFactory = wonderBlocksCore.useUniqueIdWithMock();
|
|
31169
|
+
const answerString = selectedAnswers.length > 1 ?
|
|
31170
|
+
// always need `ngettext` for variable numbers even if we don't use the singular, see https://khanacademy.slack.com/archives/C0918TZ5G/p1700163024293079
|
|
31171
|
+
i18n__namespace.ngettext("%(num)s answer", "%(num)s answers", selectedAnswers.length) : selectedAnswers[0];
|
|
31172
|
+
|
|
31173
|
+
// It should no longer be possible to interact with an answer after it
|
|
31174
|
+
// has been labeled (answered) correctly.
|
|
31175
|
+
const correct = showCorrectness === "correct";
|
|
31176
|
+
const incorrect = showCorrectness === "incorrect";
|
|
31177
|
+
const bringToFront = {
|
|
31178
|
+
boxShadow: `0 8px 8px ${Color__default["default"].offBlack64}`,
|
|
31179
|
+
zIndex: 1000
|
|
31180
|
+
};
|
|
31181
|
+
return /*#__PURE__*/React__namespace.createElement(reactPopper.Popper, {
|
|
31182
|
+
placement: side,
|
|
31183
|
+
referenceElement: markerRef,
|
|
31184
|
+
modifiers: [{
|
|
31185
|
+
name: "preventOverflow",
|
|
31186
|
+
options: {
|
|
31187
|
+
rootBoundary: "viewport"
|
|
31188
|
+
}
|
|
31189
|
+
}]
|
|
31190
|
+
}, _ref => {
|
|
31191
|
+
let {
|
|
31192
|
+
ref,
|
|
31193
|
+
style: popperStyle
|
|
31194
|
+
} = _ref;
|
|
31195
|
+
return /*#__PURE__*/React__namespace.createElement(Pill__default["default"], {
|
|
31196
|
+
size: "large",
|
|
31197
|
+
kind: "accent",
|
|
31198
|
+
id: idFactory.get("perseus-label-image-widget-answer-pill"),
|
|
31199
|
+
onClick: correct ? undefined : onClick,
|
|
31200
|
+
ref: ref,
|
|
31201
|
+
style: [style, popperStyle, {
|
|
31202
|
+
":hover": bringToFront,
|
|
31203
|
+
":focus": bringToFront
|
|
31204
|
+
}, correct && styles$i.correct, incorrect && styles$i.incorrect]
|
|
31205
|
+
}, /*#__PURE__*/React__namespace.createElement(Renderer, {
|
|
31206
|
+
content: answerString
|
|
31207
|
+
}));
|
|
31208
|
+
});
|
|
31209
|
+
};
|
|
31210
|
+
const styles$i = aphrodite.StyleSheet.create({
|
|
31211
|
+
correct: {
|
|
31212
|
+
backgroundColor: "#00880b" // WB green darkened by 18%
|
|
31213
|
+
},
|
|
31214
|
+
|
|
31215
|
+
incorrect: {
|
|
31216
|
+
backgroundColor: Color__default["default"].offBlack64
|
|
31217
|
+
}
|
|
31218
|
+
});
|
|
31219
|
+
|
|
31220
|
+
const strings = {
|
|
31221
|
+
hideAnswersToggleLabel: i18n__namespace._("Hide answer choices")
|
|
31222
|
+
};
|
|
31223
|
+
|
|
31224
|
+
const HideAnswersToggle = props => {
|
|
31225
|
+
const idFactory = wonderBlocksCore.useUniqueIdWithMock();
|
|
31226
|
+
const switchId = idFactory.get("hide-answers-toggle");
|
|
31227
|
+
const labelId = idFactory.get("hide-answers-label");
|
|
31228
|
+
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
31229
|
+
style: styles$h.switchWrapper
|
|
31230
|
+
}, /*#__PURE__*/React__namespace.createElement(Switch__default["default"], {
|
|
31231
|
+
id: switchId,
|
|
31232
|
+
checked: props.areAnswersHidden,
|
|
31233
|
+
onChange: props.onChange,
|
|
31234
|
+
"aria-labelledby": labelId
|
|
31235
|
+
}), /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
|
|
31236
|
+
id: labelId,
|
|
31237
|
+
htmlFor: switchId
|
|
31238
|
+
}, strings.hideAnswersToggleLabel));
|
|
31239
|
+
};
|
|
31240
|
+
const styles$h = aphrodite.StyleSheet.create({
|
|
31241
|
+
switchWrapper: {
|
|
31242
|
+
display: "flex",
|
|
31243
|
+
flexDirection: "row",
|
|
31244
|
+
flexWrap: "wrap-reverse",
|
|
31245
|
+
alignItems: "center",
|
|
31246
|
+
gap: "0.5em",
|
|
31247
|
+
marginTop: "1em"
|
|
31248
|
+
}
|
|
31249
|
+
});
|
|
31250
|
+
|
|
31147
31251
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
31252
|
+
function shouldReduceMotion() {
|
|
31253
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
31254
|
+
return !mediaQuery || mediaQuery.matches;
|
|
31255
|
+
}
|
|
31148
31256
|
class Marker extends React__namespace.Component {
|
|
31149
31257
|
// The marker icon element.
|
|
31150
31258
|
|
|
31151
31259
|
static defaultProps = {
|
|
31152
31260
|
selected: []
|
|
31153
31261
|
};
|
|
31154
|
-
state = {
|
|
31155
|
-
isFocused: false
|
|
31156
|
-
};
|
|
31157
|
-
handleFocus() {
|
|
31158
|
-
this.setState({
|
|
31159
|
-
isFocused: true
|
|
31160
|
-
});
|
|
31161
|
-
}
|
|
31162
|
-
handleBlur() {
|
|
31163
|
-
this.setState({
|
|
31164
|
-
isFocused: false
|
|
31165
|
-
});
|
|
31166
|
-
}
|
|
31167
31262
|
renderIcon() {
|
|
31168
31263
|
const {
|
|
31169
31264
|
selected,
|
|
31170
31265
|
showCorrectness,
|
|
31171
31266
|
showSelected,
|
|
31172
|
-
showPulsate
|
|
31267
|
+
showPulsate,
|
|
31268
|
+
focused
|
|
31173
31269
|
} = this.props;
|
|
31174
|
-
const {
|
|
31175
|
-
isFocused
|
|
31176
|
-
} = this.state;
|
|
31177
31270
|
|
|
31178
31271
|
// Only a single marker may be "selected" at a time.
|
|
31179
31272
|
// `showSelected` is a controlled prop, that may be set to `true` for
|
|
31180
31273
|
// one marker at a time.
|
|
31181
|
-
// `
|
|
31274
|
+
// `focused` is a controlled state, driven by focus events, and may
|
|
31182
31275
|
// only be `true` when there's no answer choices popup visible, and
|
|
31183
31276
|
// keyboard focus is given to the marker.
|
|
31184
|
-
const isSelected = showSelected ||
|
|
31277
|
+
const isSelected = showSelected || focused;
|
|
31185
31278
|
let innerIcon;
|
|
31186
31279
|
let iconStyles;
|
|
31187
31280
|
if (showCorrectness) {
|
|
@@ -31191,17 +31284,28 @@ class Marker extends React__namespace.Component {
|
|
|
31191
31284
|
color: Color__default["default"].white
|
|
31192
31285
|
});
|
|
31193
31286
|
if (showCorrectness === "correct") {
|
|
31194
|
-
iconStyles = [styles$g.markerCorrect, isSelected && styles$g.
|
|
31287
|
+
iconStyles = [styles$g.markerGraded, styles$g.markerCorrect, isSelected && styles$g.markerSelected];
|
|
31195
31288
|
} else {
|
|
31196
|
-
iconStyles = [styles$g.markerIncorrect, isSelected && styles$g.
|
|
31289
|
+
iconStyles = [styles$g.markerGraded, styles$g.markerIncorrect, isSelected && styles$g.markerSelected];
|
|
31197
31290
|
}
|
|
31291
|
+
} else if (focused) {
|
|
31292
|
+
iconStyles = [styles$g.markerFocused, selected && selected.length > 0 && styles$g.markerFilled, showSelected && styles$g.markerSelected];
|
|
31198
31293
|
} else if (selected && selected.length > 0) {
|
|
31199
|
-
iconStyles = [styles$g.markerFilled, isSelected && styles$g.
|
|
31294
|
+
iconStyles = [styles$g.markerFilled, isSelected && styles$g.markerSelected];
|
|
31295
|
+
} else if (isSelected) {
|
|
31296
|
+
iconStyles = [styles$g.markerSelected];
|
|
31200
31297
|
} else {
|
|
31201
|
-
iconStyles = [
|
|
31298
|
+
iconStyles = [styles$g.markerPulsateBase, shouldReduceMotion() ? showPulsate && styles$g.markerUnfilledPulsateOnce : showPulsate && styles$g.markerUnfilledPulsateInfinite];
|
|
31202
31299
|
}
|
|
31203
|
-
|
|
31204
|
-
|
|
31300
|
+
if (isSelected && selected && selected.length === 0) {
|
|
31301
|
+
innerIcon = /*#__PURE__*/React__namespace.createElement(Icon, {
|
|
31302
|
+
icon: iconChevronDown,
|
|
31303
|
+
size: 8,
|
|
31304
|
+
color: Color__default["default"].white
|
|
31305
|
+
});
|
|
31306
|
+
}
|
|
31307
|
+
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
31308
|
+
style: [styles$g.markerIcon, iconStyles],
|
|
31205
31309
|
ref: node => this._icon = node
|
|
31206
31310
|
}, innerIcon);
|
|
31207
31311
|
}
|
|
@@ -31224,26 +31328,18 @@ class Marker extends React__namespace.Component {
|
|
|
31224
31328
|
top: `${y}%`
|
|
31225
31329
|
},
|
|
31226
31330
|
tabIndex: isDisabled ? -1 : 0,
|
|
31227
|
-
onFocus:
|
|
31228
|
-
onBlur:
|
|
31229
|
-
|
|
31230
|
-
|
|
31231
|
-
|
|
31232
|
-
|
|
31233
|
-
,
|
|
31331
|
+
onFocus: this.props.onFocus,
|
|
31332
|
+
onBlur: this.props.onBlur,
|
|
31333
|
+
onClick: e => {
|
|
31334
|
+
e.preventDefault();
|
|
31335
|
+
this.props.onClick(e);
|
|
31336
|
+
},
|
|
31234
31337
|
onKeyDown: e => this.props.onKeyDown(e)
|
|
31235
31338
|
}, this.renderIcon());
|
|
31236
31339
|
}
|
|
31237
31340
|
}
|
|
31238
|
-
const markerColor = "#1865f2";
|
|
31239
|
-
const selectedColor = "#2552b0";
|
|
31240
|
-
const activeColor = selectedColor;
|
|
31241
|
-
const correctColor = "#00a60e";
|
|
31242
|
-
const correctActiveColor = "#167b1f";
|
|
31243
|
-
const incorrectColor = "#909195";
|
|
31244
|
-
const incorrectActiveColor = "#6c6e73";
|
|
31245
|
-
const markerShadowColor = "rgba(33, 36, 44, 0.32)";
|
|
31246
31341
|
const lightShadowColor = "rgba(33, 36, 44, 0.16)";
|
|
31342
|
+
const markerSize = 20;
|
|
31247
31343
|
const styles$g = aphrodite.StyleSheet.create({
|
|
31248
31344
|
unstyledButton: {
|
|
31249
31345
|
padding: 0,
|
|
@@ -31256,9 +31352,12 @@ const styles$g = aphrodite.StyleSheet.create({
|
|
|
31256
31352
|
},
|
|
31257
31353
|
marker: {
|
|
31258
31354
|
position: "absolute",
|
|
31355
|
+
outline: "none",
|
|
31259
31356
|
// Center marker position based on it's maximum size.
|
|
31260
|
-
width:
|
|
31261
|
-
height:
|
|
31357
|
+
width: markerSize,
|
|
31358
|
+
height: markerSize,
|
|
31359
|
+
backgroundColor: Color__default["default"].white,
|
|
31360
|
+
borderRadius: 30,
|
|
31262
31361
|
marginLeft: -15,
|
|
31263
31362
|
marginTop: -15
|
|
31264
31363
|
},
|
|
@@ -31268,136 +31367,92 @@ const styles$g = aphrodite.StyleSheet.create({
|
|
|
31268
31367
|
// The base and unfilled marker style.
|
|
31269
31368
|
markerIcon: {
|
|
31270
31369
|
display: "flex",
|
|
31271
|
-
|
|
31370
|
+
alignItems: "center",
|
|
31371
|
+
justifyContent: "center",
|
|
31272
31372
|
boxSizing: "content-box",
|
|
31273
|
-
width:
|
|
31274
|
-
height:
|
|
31275
|
-
|
|
31276
|
-
marginLeft: 5,
|
|
31373
|
+
width: markerSize,
|
|
31374
|
+
height: markerSize,
|
|
31375
|
+
marginLeft: 2,
|
|
31277
31376
|
cursor: "pointer",
|
|
31278
|
-
|
|
31279
|
-
|
|
31280
|
-
|
|
31281
|
-
|
|
31377
|
+
border: `2px solid ${Color__default["default"].offBlack64}`,
|
|
31378
|
+
borderRadius: markerSize,
|
|
31379
|
+
boxShadow: `0 8px 8px ${Color__default["default"].offBlack8}`,
|
|
31380
|
+
":hover": {
|
|
31381
|
+
outline: `2px solid ${Color__default["default"].blue}`,
|
|
31382
|
+
outlineOffset: 2
|
|
31383
|
+
}
|
|
31282
31384
|
},
|
|
31283
|
-
|
|
31385
|
+
// The animation that presents the marker to the learner
|
|
31386
|
+
markerPulsateBase: {
|
|
31284
31387
|
animationName: {
|
|
31285
31388
|
"0%": {
|
|
31286
|
-
transform: "scale(1)"
|
|
31389
|
+
transform: "scale(1)",
|
|
31390
|
+
backgroundColor: Color__default["default"].blue
|
|
31287
31391
|
},
|
|
31288
31392
|
"100%": {
|
|
31289
|
-
transform: "scale(1.
|
|
31393
|
+
transform: "scale(1.3)",
|
|
31394
|
+
backgroundColor: Color__default["default"].blue
|
|
31290
31395
|
}
|
|
31291
31396
|
},
|
|
31292
31397
|
animationDirection: "alternate",
|
|
31293
31398
|
animationDuration: "0.8s",
|
|
31294
|
-
animationIterationCount: "infinite",
|
|
31295
31399
|
animationTimingFunction: "ease-in",
|
|
31296
|
-
transformOrigin: "50% 50%"
|
|
31400
|
+
transformOrigin: "50% 50%",
|
|
31401
|
+
animationIterationCount: "0"
|
|
31297
31402
|
},
|
|
31298
|
-
|
|
31299
|
-
"
|
|
31300
|
-
|
|
31301
|
-
|
|
31302
|
-
position: "absolute",
|
|
31303
|
-
width: 20,
|
|
31304
|
-
height: 20,
|
|
31305
|
-
marginLeft: -4,
|
|
31306
|
-
marginTop: -4,
|
|
31307
|
-
border: `solid 2px ${selectedColor}`,
|
|
31308
|
-
borderRadius: 20
|
|
31309
|
-
},
|
|
31310
|
-
":active": {
|
|
31311
|
-
backgroundColor: activeColor,
|
|
31312
|
-
boxShadow: "none",
|
|
31313
|
-
"::before": {
|
|
31314
|
-
display: "none"
|
|
31315
|
-
}
|
|
31403
|
+
markerUnfilledPulsateInfinite: {
|
|
31404
|
+
animationIterationCount: "infinite",
|
|
31405
|
+
":hover": {
|
|
31406
|
+
outline: `0px solid ${Color__default["default"].blue}`
|
|
31316
31407
|
}
|
|
31317
31408
|
},
|
|
31318
|
-
|
|
31319
|
-
|
|
31320
|
-
|
|
31321
|
-
// Center icon within marker.
|
|
31322
|
-
marginLeft: 9,
|
|
31323
|
-
borderRadius: 8,
|
|
31324
|
-
boxShadow: `0 1px 1px 0 ${lightShadowColor}`
|
|
31409
|
+
markerUnfilledPulsateOnce: {
|
|
31410
|
+
// Doing the animation twice lets it ease-in and ease-out
|
|
31411
|
+
animationIterationCount: "2"
|
|
31325
31412
|
},
|
|
31326
|
-
|
|
31327
|
-
|
|
31328
|
-
|
|
31329
|
-
|
|
31330
|
-
|
|
31331
|
-
|
|
31332
|
-
|
|
31333
|
-
|
|
31334
|
-
|
|
31335
|
-
|
|
31336
|
-
|
|
31337
|
-
|
|
31338
|
-
|
|
31339
|
-
|
|
31340
|
-
boxShadow: "none",
|
|
31341
|
-
"::before": {
|
|
31342
|
-
display: "none"
|
|
31343
|
-
}
|
|
31413
|
+
markerFocused: {
|
|
31414
|
+
outline: `2px solid ${Color__default["default"].blue}`,
|
|
31415
|
+
outlineOffset: 2
|
|
31416
|
+
},
|
|
31417
|
+
// The learner is making an initial selection
|
|
31418
|
+
markerSelected: {
|
|
31419
|
+
boxShadow: `0 8px 8px ${Color__default["default"].offBlack8}`,
|
|
31420
|
+
border: `solid 4px ${Color__default["default"].white}`,
|
|
31421
|
+
backgroundColor: Color__default["default"].blue,
|
|
31422
|
+
borderRadius: markerSize,
|
|
31423
|
+
transform: "rotate(180deg)",
|
|
31424
|
+
":hover": {
|
|
31425
|
+
outline: `2px solid ${Color__default["default"].blue}`,
|
|
31426
|
+
outlineOffset: -2
|
|
31344
31427
|
}
|
|
31345
31428
|
},
|
|
31346
|
-
|
|
31347
|
-
|
|
31348
|
-
|
|
31349
|
-
|
|
31350
|
-
|
|
31351
|
-
|
|
31352
|
-
|
|
31353
|
-
|
|
31429
|
+
// The learner has made a selection
|
|
31430
|
+
markerFilled: {
|
|
31431
|
+
width: markerSize - 2,
|
|
31432
|
+
height: markerSize - 2,
|
|
31433
|
+
borderRadius: markerSize - 2,
|
|
31434
|
+
backgroundColor: "#ECF3FE",
|
|
31435
|
+
marginLeft: 3,
|
|
31436
|
+
marginTop: 0,
|
|
31437
|
+
border: `4px solid ${Color__default["default"].blue}`,
|
|
31354
31438
|
boxShadow: `0 1px 1px 0 ${lightShadowColor}`
|
|
31355
31439
|
},
|
|
31356
|
-
|
|
31357
|
-
|
|
31358
|
-
|
|
31359
|
-
display: "inline-block",
|
|
31360
|
-
position: "absolute",
|
|
31361
|
-
width: 28,
|
|
31362
|
-
height: 28,
|
|
31363
|
-
border: `solid 2px ${selectedColor}`,
|
|
31364
|
-
borderRadius: 28
|
|
31365
|
-
},
|
|
31366
|
-
":active": {
|
|
31367
|
-
backgroundColor: correctActiveColor,
|
|
31368
|
-
boxShadow: "none",
|
|
31369
|
-
"::before": {
|
|
31370
|
-
display: "none"
|
|
31371
|
-
}
|
|
31372
|
-
}
|
|
31373
|
-
},
|
|
31374
|
-
markerIncorrect: {
|
|
31375
|
-
width: 24,
|
|
31376
|
-
height: 24,
|
|
31440
|
+
markerGraded: {
|
|
31441
|
+
width: markerSize,
|
|
31442
|
+
height: markerSize,
|
|
31377
31443
|
marginLeft: 1,
|
|
31378
31444
|
marginTop: 1,
|
|
31379
31445
|
justifyContent: "center",
|
|
31380
31446
|
alignItems: "center",
|
|
31381
|
-
|
|
31382
|
-
boxShadow: `0
|
|
31447
|
+
border: `2px solid ${Color__default["default"].white}`,
|
|
31448
|
+
boxShadow: `0 8px 8px ${Color__default["default"].offBlack8}`
|
|
31383
31449
|
},
|
|
31384
|
-
|
|
31385
|
-
"
|
|
31386
|
-
|
|
31387
|
-
|
|
31388
|
-
|
|
31389
|
-
|
|
31390
|
-
height: 28,
|
|
31391
|
-
border: `solid 2px ${selectedColor}`,
|
|
31392
|
-
borderRadius: 28
|
|
31393
|
-
},
|
|
31394
|
-
":active": {
|
|
31395
|
-
backgroundColor: incorrectActiveColor,
|
|
31396
|
-
boxShadow: "none",
|
|
31397
|
-
"::before": {
|
|
31398
|
-
display: "none"
|
|
31399
|
-
}
|
|
31400
|
-
}
|
|
31450
|
+
markerCorrect: {
|
|
31451
|
+
background: "#00880b" // WB green darkened by 18%
|
|
31452
|
+
},
|
|
31453
|
+
|
|
31454
|
+
markerIncorrect: {
|
|
31455
|
+
background: Color__default["default"].offBlack64
|
|
31401
31456
|
}
|
|
31402
31457
|
});
|
|
31403
31458
|
|
|
@@ -31481,7 +31536,20 @@ class LabelImage extends React__namespace.Component {
|
|
|
31481
31536
|
/**
|
|
31482
31537
|
* Determine the image side given a marker position (as percent of size).
|
|
31483
31538
|
*/
|
|
31484
|
-
static imageSideForMarkerPosition(x, y) {
|
|
31539
|
+
static imageSideForMarkerPosition(x, y, preferredDirection) {
|
|
31540
|
+
// Special handling for preferred size
|
|
31541
|
+
if (preferredDirection && preferredDirection !== "NONE") {
|
|
31542
|
+
if (preferredDirection === "LEFT" && x > 20) {
|
|
31543
|
+
return "right";
|
|
31544
|
+
} else if (preferredDirection === "RIGHT" && x < 80) {
|
|
31545
|
+
return "left";
|
|
31546
|
+
} else if (preferredDirection === "UP" && y > 20) {
|
|
31547
|
+
return "bottom";
|
|
31548
|
+
} else if (preferredDirection === "DOWN" && y < 80) {
|
|
31549
|
+
return "top";
|
|
31550
|
+
}
|
|
31551
|
+
}
|
|
31552
|
+
|
|
31485
31553
|
// Special handling for when marker is positioned near the horizontal
|
|
31486
31554
|
// edges of the image. We want to ensure the returned side would not
|
|
31487
31555
|
// result in a popup rendering that may overflow outside the page.
|
|
@@ -31632,18 +31700,12 @@ class LabelImage extends React__namespace.Component {
|
|
|
31632
31700
|
super(props);
|
|
31633
31701
|
this._markers = [];
|
|
31634
31702
|
this.state = {
|
|
31635
|
-
|
|
31636
|
-
|
|
31703
|
+
activeMarkerIndex: -1,
|
|
31704
|
+
focusedMarkerIndex: -1,
|
|
31705
|
+
markersInteracted: false,
|
|
31706
|
+
hideAnswers: false
|
|
31637
31707
|
};
|
|
31638
31708
|
}
|
|
31639
|
-
componentDidMount() {
|
|
31640
|
-
document.addEventListener("click", this.handleDocumentClick, true);
|
|
31641
|
-
document.addEventListener("keydown", this.handleDocumentKeyDown, true);
|
|
31642
|
-
}
|
|
31643
|
-
componentWillUnmount() {
|
|
31644
|
-
document.removeEventListener("click", this.handleDocumentClick, true);
|
|
31645
|
-
document.removeEventListener("keydown", this.handleDocumentKeyDown, true);
|
|
31646
|
-
}
|
|
31647
31709
|
simpleValidate(rubric) {
|
|
31648
31710
|
return LabelImage.validate(this.getUserInput(), rubric);
|
|
31649
31711
|
}
|
|
@@ -31680,17 +31742,17 @@ class LabelImage extends React__namespace.Component {
|
|
|
31680
31742
|
}
|
|
31681
31743
|
dismissMarkerPopup() {
|
|
31682
31744
|
const {
|
|
31683
|
-
|
|
31745
|
+
activeMarkerIndex
|
|
31684
31746
|
} = this.state;
|
|
31685
31747
|
|
|
31686
31748
|
// No popup should be open if there's no selected marker.
|
|
31687
|
-
if (
|
|
31749
|
+
if (activeMarkerIndex === -1) {
|
|
31688
31750
|
return;
|
|
31689
31751
|
}
|
|
31690
31752
|
this.setState({
|
|
31691
|
-
|
|
31753
|
+
activeMarkerIndex: -1
|
|
31692
31754
|
}, () => {
|
|
31693
|
-
const marker = this._markers[
|
|
31755
|
+
const marker = this._markers[activeMarkerIndex];
|
|
31694
31756
|
// Set focus on the just-deselected-marker, to enable to resume
|
|
31695
31757
|
// navigating between the markers using the keyboard.
|
|
31696
31758
|
if (marker) {
|
|
@@ -31698,40 +31760,6 @@ class LabelImage extends React__namespace.Component {
|
|
|
31698
31760
|
}
|
|
31699
31761
|
});
|
|
31700
31762
|
}
|
|
31701
|
-
handleDocumentClick = e => {
|
|
31702
|
-
// Dismiss open popup with answer choices if user clicks outside it.
|
|
31703
|
-
if (this._selectedMarkerPopup && this._answerChoices) {
|
|
31704
|
-
const answerChoices = ReactDOM__namespace.findDOMNode(this._answerChoices);
|
|
31705
|
-
const targetElement = e.target;
|
|
31706
|
-
// HACK(michaelpolyak): We want to determine if the click target is
|
|
31707
|
-
// contained within the popup layer. As there's no public interface
|
|
31708
|
-
// to get this layer from the popup, we traverse several levels of
|
|
31709
|
-
// answer choices parents to test whether the click target is
|
|
31710
|
-
// contained within.
|
|
31711
|
-
const containsEventTarget = function (element) {
|
|
31712
|
-
let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
|
|
31713
|
-
return element && (element.contains(targetElement) || depth > 0 && containsEventTarget(element.parentElement, depth - 1));
|
|
31714
|
-
};
|
|
31715
|
-
if (!containsEventTarget(answerChoices)) {
|
|
31716
|
-
// Close popup and set focus back to the marker for which it was
|
|
31717
|
-
// open.
|
|
31718
|
-
this.dismissMarkerPopup();
|
|
31719
|
-
}
|
|
31720
|
-
}
|
|
31721
|
-
};
|
|
31722
|
-
handleDocumentKeyDown = e => {
|
|
31723
|
-
// Dismiss open popup with answer choices if user presses Escape key.
|
|
31724
|
-
if (this._selectedMarkerPopup && e.keyCode === 27) {
|
|
31725
|
-
// Ensure other listeners are not triggered on key down event that
|
|
31726
|
-
// closes the popup, as this will also dismiss the modal that the
|
|
31727
|
-
// widget may be rendered within.
|
|
31728
|
-
e.stopPropagation();
|
|
31729
|
-
|
|
31730
|
-
// Close popup and set focus back to the marker for which it was
|
|
31731
|
-
// open.
|
|
31732
|
-
this.dismissMarkerPopup();
|
|
31733
|
-
}
|
|
31734
|
-
};
|
|
31735
31763
|
handleMarkerChange(index, marker) {
|
|
31736
31764
|
const {
|
|
31737
31765
|
markers,
|
|
@@ -31751,16 +31779,15 @@ class LabelImage extends React__namespace.Component {
|
|
|
31751
31779
|
markers: updatedMarkers
|
|
31752
31780
|
});
|
|
31753
31781
|
}
|
|
31754
|
-
|
|
31782
|
+
activateMarker(index) {
|
|
31755
31783
|
const {
|
|
31756
|
-
|
|
31784
|
+
activeMarkerIndex
|
|
31757
31785
|
} = this.state;
|
|
31758
|
-
e.preventDefault();
|
|
31759
31786
|
|
|
31760
31787
|
// Select the marker, revealing answer choices.
|
|
31761
|
-
if (
|
|
31788
|
+
if (activeMarkerIndex !== index) {
|
|
31762
31789
|
this.setState({
|
|
31763
|
-
|
|
31790
|
+
activeMarkerIndex: index,
|
|
31764
31791
|
markersInteracted: true
|
|
31765
31792
|
}, () => {
|
|
31766
31793
|
// A delay is required to allow answer choices to first render
|
|
@@ -31830,10 +31857,10 @@ class LabelImage extends React__namespace.Component {
|
|
|
31830
31857
|
} = this.props;
|
|
31831
31858
|
|
|
31832
31859
|
// Compile the user selected answer choices.
|
|
31833
|
-
const selected = choices.filter((
|
|
31860
|
+
const selected = choices.filter((_, index) => selection[index]);
|
|
31834
31861
|
this.handleMarkerChange(index, {
|
|
31835
31862
|
...markers[index],
|
|
31836
|
-
selected
|
|
31863
|
+
selected: selected.length ? selected : undefined
|
|
31837
31864
|
});
|
|
31838
31865
|
if (!multipleAnswers) {
|
|
31839
31866
|
this.dismissMarkerPopup();
|
|
@@ -31855,50 +31882,80 @@ class LabelImage extends React__namespace.Component {
|
|
|
31855
31882
|
checked: selected ? selected.includes(choice) : false
|
|
31856
31883
|
})),
|
|
31857
31884
|
multipleSelect: multipleAnswers,
|
|
31858
|
-
onChange: selection =>
|
|
31885
|
+
onChange: selection => {
|
|
31886
|
+
this.props.analytics?.onAnalyticsEvent({
|
|
31887
|
+
type: "perseus:label-image:choiced-interacted-with",
|
|
31888
|
+
payload: null
|
|
31889
|
+
});
|
|
31890
|
+
this.handleAnswerChoicesChangeForMarker(index, selection);
|
|
31891
|
+
},
|
|
31859
31892
|
ref: node => this._answerChoices = node
|
|
31860
31893
|
}));
|
|
31861
31894
|
}
|
|
31862
31895
|
renderMarkers() {
|
|
31863
31896
|
const {
|
|
31864
31897
|
markers,
|
|
31865
|
-
questionCompleted
|
|
31898
|
+
questionCompleted,
|
|
31899
|
+
preferredPopoverDirection
|
|
31866
31900
|
} = this.props;
|
|
31867
31901
|
const {
|
|
31868
|
-
|
|
31902
|
+
activeMarkerIndex,
|
|
31869
31903
|
markersInteracted
|
|
31870
31904
|
} = this.state;
|
|
31871
31905
|
|
|
31872
31906
|
// Render all markers for widget.
|
|
31873
31907
|
return markers.map((marker, index) => {
|
|
31874
31908
|
const score = LabelImage.gradeMarker(marker);
|
|
31909
|
+
// Once the question is answered, show markers
|
|
31910
|
+
// with correct answers, otherwise passthrough
|
|
31911
|
+
// the correctness state.
|
|
31912
|
+
const showCorrectness = questionCompleted && score.hasAnswers && score.isCorrect ? "correct" : marker.showCorrectness;
|
|
31875
31913
|
const element = /*#__PURE__*/React__namespace.createElement(Marker, _extends({}, marker, {
|
|
31876
|
-
|
|
31877
|
-
|
|
31878
|
-
// the correctness state.
|
|
31879
|
-
showCorrectness: questionCompleted && score.hasAnswers && score.isCorrect ? "correct" : marker.showCorrectness,
|
|
31880
|
-
showSelected: index === selectedMarkerIndex,
|
|
31914
|
+
showCorrectness: showCorrectness,
|
|
31915
|
+
showSelected: index === activeMarkerIndex,
|
|
31881
31916
|
showPulsate: !markersInteracted,
|
|
31882
31917
|
key: `${marker.x}.${marker.y}`,
|
|
31883
|
-
onClick:
|
|
31918
|
+
onClick: () => {
|
|
31919
|
+
this.props.analytics?.onAnalyticsEvent({
|
|
31920
|
+
type: "perseus:label-image:marker-interacted-with",
|
|
31921
|
+
payload: null
|
|
31922
|
+
});
|
|
31923
|
+
this.activateMarker(index);
|
|
31924
|
+
},
|
|
31884
31925
|
onKeyDown: e => this.handleMarkerKeyDown(index, e),
|
|
31885
|
-
ref: node => this._markers[index] = node
|
|
31926
|
+
ref: node => this._markers[index] = node,
|
|
31927
|
+
focused: index === this.state.focusedMarkerIndex,
|
|
31928
|
+
onFocus: () => {
|
|
31929
|
+
this.setState({
|
|
31930
|
+
focusedMarkerIndex: index
|
|
31931
|
+
});
|
|
31932
|
+
},
|
|
31933
|
+
onBlur: () => {
|
|
31934
|
+
if (index === this.state.focusedMarkerIndex) {
|
|
31935
|
+
this.setState({
|
|
31936
|
+
focusedMarkerIndex: -1
|
|
31937
|
+
});
|
|
31938
|
+
}
|
|
31939
|
+
}
|
|
31886
31940
|
}));
|
|
31887
31941
|
|
|
31888
|
-
// The user selected marker is wrapped with a popup that shows its
|
|
31889
|
-
// answer choices, otherwise it's returned as is.
|
|
31890
|
-
if (index !== selectedMarkerIndex) {
|
|
31891
|
-
return element;
|
|
31892
|
-
}
|
|
31893
|
-
|
|
31894
31942
|
// Determine whether page is rendered in a narrow browser window.
|
|
31895
31943
|
const isNarrowPage = window.matchMedia(mediaQueries.xsOrSmaller.replace("@media ", "")).matches;
|
|
31944
|
+
|
|
31945
|
+
// Determine whether the image is wider than it is tall.
|
|
31946
|
+
const isWideImage = this.props.imageWidth / 2 > this.props.imageHeight;
|
|
31896
31947
|
let side;
|
|
31948
|
+
let markerPosition;
|
|
31897
31949
|
// Position popup closest to the center, preferring it renders
|
|
31898
31950
|
// entirely within the image area.
|
|
31899
|
-
if (isNarrowPage) {
|
|
31951
|
+
if (isNarrowPage || isWideImage) {
|
|
31900
31952
|
side = marker.y > 50 ? "top" : "bottom";
|
|
31953
|
+
markerPosition = marker.y > 50 ? "bottom" : "top";
|
|
31901
31954
|
} else {
|
|
31955
|
+
markerPosition = LabelImage.imageSideForMarkerPosition(marker.x, marker.y, preferredPopoverDirection);
|
|
31956
|
+
if (markerPosition === "center") {
|
|
31957
|
+
markerPosition = "bottom";
|
|
31958
|
+
}
|
|
31902
31959
|
// This mirrors the calculated side of where the marker is
|
|
31903
31960
|
// located within the image, so that popup appears closer to
|
|
31904
31961
|
// the image center.
|
|
@@ -31906,24 +31963,44 @@ class LabelImage extends React__namespace.Component {
|
|
|
31906
31963
|
left: "right",
|
|
31907
31964
|
top: "bottom",
|
|
31908
31965
|
right: "left",
|
|
31909
|
-
bottom: "top"
|
|
31910
|
-
|
|
31911
|
-
}[LabelImage.imageSideForMarkerPosition(marker.x, marker.y)];
|
|
31966
|
+
bottom: "top"
|
|
31967
|
+
}[markerPosition];
|
|
31912
31968
|
}
|
|
31969
|
+
const answerChoicesActive = index === activeMarkerIndex;
|
|
31970
|
+
const adjustPopoverDistance = {
|
|
31971
|
+
[side]: 15 // move the popover closer to the marker
|
|
31972
|
+
};
|
|
31913
31973
|
|
|
31914
|
-
|
|
31915
|
-
|
|
31916
|
-
|
|
31917
|
-
|
|
31918
|
-
|
|
31974
|
+
const adjustPillDistance = {
|
|
31975
|
+
[`margin${markerPosition.charAt(0).toUpperCase() + markerPosition.slice(1)}`]: 5 // move pill further from marker
|
|
31976
|
+
};
|
|
31977
|
+
|
|
31978
|
+
const showAnswerChoice = !answerChoicesActive && !this.state.hideAnswers;
|
|
31979
|
+
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
31980
|
+
key: `answers-${marker.x}.${marker.y}`
|
|
31981
|
+
}, /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.Popover, {
|
|
31919
31982
|
content: () => /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.PopoverContentCore, {
|
|
31920
|
-
style: styles$f.choicesPopover
|
|
31983
|
+
style: [adjustPopoverDistance, styles$f.choicesPopover]
|
|
31921
31984
|
}, this.renderAnswerChoicesForMarker(index, marker)),
|
|
31922
31985
|
placement: side,
|
|
31923
|
-
|
|
31924
|
-
|
|
31925
|
-
ref: node => this._selectedMarkerPopup = node
|
|
31926
|
-
|
|
31986
|
+
onClose: () => this.dismissMarkerPopup(),
|
|
31987
|
+
opened: answerChoicesActive,
|
|
31988
|
+
ref: node => this._selectedMarkerPopup = node,
|
|
31989
|
+
showTail: false,
|
|
31990
|
+
dismissEnabled: true
|
|
31991
|
+
}, element), !!marker.selected && showAnswerChoice && /*#__PURE__*/React__namespace.createElement(AnswerPill, {
|
|
31992
|
+
selectedAnswers: marker.selected,
|
|
31993
|
+
showCorrectness: showCorrectness,
|
|
31994
|
+
markerRef:
|
|
31995
|
+
// this throws a warning because it's inadvisable to
|
|
31996
|
+
// call ReactDOM fns in render. we aren't storing
|
|
31997
|
+
// actual refs in this array, so not much I can do
|
|
31998
|
+
// outside of a refactor.
|
|
31999
|
+
ReactDOM__namespace.findDOMNode(this._markers[index]),
|
|
32000
|
+
side: side,
|
|
32001
|
+
onClick: () => this.activateMarker(index),
|
|
32002
|
+
style: adjustPillDistance
|
|
32003
|
+
}));
|
|
31927
32004
|
});
|
|
31928
32005
|
}
|
|
31929
32006
|
renderInstructions() {
|
|
@@ -31958,7 +32035,7 @@ class LabelImage extends React__namespace.Component {
|
|
|
31958
32035
|
imageHeight
|
|
31959
32036
|
} = this.props;
|
|
31960
32037
|
const {
|
|
31961
|
-
|
|
32038
|
+
activeMarkerIndex
|
|
31962
32039
|
} = this.state;
|
|
31963
32040
|
return /*#__PURE__*/React__namespace.createElement("div", null, this.renderInstructions(), /*#__PURE__*/React__namespace.createElement("div", {
|
|
31964
32041
|
className: aphrodite.css(styles$f.markersCanvas),
|
|
@@ -31974,7 +32051,7 @@ class LabelImage extends React__namespace.Component {
|
|
|
31974
32051
|
// dismiss the popup. If the image is larger in size
|
|
31975
32052
|
// than its rendered in the widget, this would
|
|
31976
32053
|
// result in a zoom of the image.
|
|
31977
|
-
|
|
32054
|
+
activeMarkerIndex !== -1 && styles$f.imageInteractionDisabled)
|
|
31978
32055
|
}, /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null, _ref => {
|
|
31979
32056
|
let {
|
|
31980
32057
|
setAssetStatus
|
|
@@ -31986,7 +32063,18 @@ class LabelImage extends React__namespace.Component {
|
|
|
31986
32063
|
height: imageHeight,
|
|
31987
32064
|
setAssetStatus: setAssetStatus
|
|
31988
32065
|
});
|
|
31989
|
-
})), this.renderMarkers())
|
|
32066
|
+
})), this.renderMarkers()), /*#__PURE__*/React__namespace.createElement(HideAnswersToggle, {
|
|
32067
|
+
areAnswersHidden: this.state.hideAnswers,
|
|
32068
|
+
onChange: hideAnswers => {
|
|
32069
|
+
this.props.analytics?.onAnalyticsEvent({
|
|
32070
|
+
type: "perseus:label-image:toggle-answers-hidden",
|
|
32071
|
+
payload: null
|
|
32072
|
+
});
|
|
32073
|
+
this.setState({
|
|
32074
|
+
hideAnswers
|
|
32075
|
+
});
|
|
32076
|
+
}
|
|
32077
|
+
}));
|
|
31990
32078
|
}
|
|
31991
32079
|
}
|
|
31992
32080
|
const styles$f = aphrodite.StyleSheet.create({
|
|
@@ -32044,10 +32132,26 @@ const styles$f = aphrodite.StyleSheet.create({
|
|
|
32044
32132
|
padding: 0
|
|
32045
32133
|
}
|
|
32046
32134
|
});
|
|
32135
|
+
const LabelImageWithDependencies = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
32136
|
+
const deps = useDependencies();
|
|
32137
|
+
return /*#__PURE__*/React__namespace.createElement(LabelImage, _extends({
|
|
32138
|
+
ref: ref,
|
|
32139
|
+
analytics: deps.analytics
|
|
32140
|
+
}, props));
|
|
32141
|
+
});
|
|
32142
|
+
|
|
32143
|
+
// HACK: Propogate "static" methods onto our wrapper component.
|
|
32144
|
+
// In the future we should adjust client apps to not depend on these static
|
|
32145
|
+
// methods and instead adjust Peresus to provide these facilities through
|
|
32146
|
+
// instance methods on our Renderers.
|
|
32147
|
+
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
32148
|
+
LabelImageWithDependencies.validate = LabelImage.validate;
|
|
32149
|
+
// @ts-expect-error - TS2339 - Property 'gradeMarker' does not exist on type
|
|
32150
|
+
LabelImageWithDependencies.gradeMarker = LabelImage.gradeMarker;
|
|
32047
32151
|
var LabelImage$1 = {
|
|
32048
32152
|
name: "label-image",
|
|
32049
32153
|
displayName: "Label Image",
|
|
32050
|
-
widget:
|
|
32154
|
+
widget: LabelImageWithDependencies,
|
|
32051
32155
|
accessible: true,
|
|
32052
32156
|
isLintable: true
|
|
32053
32157
|
};
|
|
@@ -44216,7 +44320,7 @@ const init = function (options) {
|
|
|
44216
44320
|
|
|
44217
44321
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44218
44322
|
const libName = "@khanacademy/perseus";
|
|
44219
|
-
const libVersion = "17.
|
|
44323
|
+
const libVersion = "17.1.0";
|
|
44220
44324
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44221
44325
|
|
|
44222
44326
|
const apiVersion = {
|