@khanacademy/perseus 19.6.2 → 19.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -29,11 +29,12 @@ import { Strut } from '@khanacademy/wonder-blocks-layout';
29
29
  import Banner from '@khanacademy/wonder-blocks-banner';
30
30
  import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-blocks-dropdown';
31
31
  import debounce$1 from 'lodash.debounce';
32
+ import { useMovablePoint, Line as Line$4, Coordinates, Mafs } from 'mafs';
33
+ import createReactClass from 'create-react-class';
32
34
  import Switch from '@khanacademy/wonder-blocks-switch';
33
35
  import { LabelMedium } from '@khanacademy/wonder-blocks-typography';
34
36
  import Pill from '@khanacademy/wonder-blocks-pill';
35
37
  import { Popper } from 'react-popper';
36
- import createReactClass from 'create-react-class';
37
38
  import Link from '@khanacademy/wonder-blocks-link';
38
39
  import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
39
40
 
@@ -1901,7 +1902,7 @@ const KhanAnswerTypes = {
1901
1902
  }
1902
1903
  };
1903
1904
 
1904
- const _excluded$4 = ["scoring"];
1905
+ const _excluded$5 = ["scoring"];
1905
1906
  const ERROR_TITLE = i18n._("Oops!");
1906
1907
  const ERROR_MESSAGE = i18n._("Sorry, I don't understand that!");
1907
1908
 
@@ -1989,7 +1990,7 @@ class Expression extends React.Component {
1989
1990
  let {
1990
1991
  scoring
1991
1992
  } = _ref,
1992
- rubric = _objectWithoutPropertiesLoose(_ref, _excluded$4);
1993
+ rubric = _objectWithoutPropertiesLoose(_ref, _excluded$5);
1993
1994
  const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {});
1994
1995
 
1995
1996
  // "scoring" is a flag that indicates when we are checking answers.
@@ -19252,6 +19253,15 @@ const isIdPathPrefix = function isIdPathPrefix(prefixArray, wholeArray) {
19252
19253
  // some return a custom-built object, some delegate to
19253
19254
  // `WidgetJsonifyDeprecated` which returns an object containing widget props
19254
19255
  // (filtered by deprecated keys)).
19256
+ /**
19257
+ * The Widget type represents the common API that the Renderer uses to interact
19258
+ * with all widgets. All widgets must implement the methods in this API, unless
19259
+ * they are marked as optional (?: ...).
19260
+ *
19261
+ * These methods are called on the widget ref and allow the renderer to
19262
+ * communicate with the individual widgets to coordinate actions such as
19263
+ * scoring, state serialization/deserialization, and focus management.
19264
+ */
19255
19265
  class Renderer extends React.Component {
19256
19266
  constructor(_props) {
19257
19267
  super(_props);
@@ -21383,7 +21393,7 @@ InputWithExamples.defaultProps = {
21383
21393
  className: ""
21384
21394
  };
21385
21395
 
21386
- const _excluded$3 = ["keypadElement", "onFocus", "value"];
21396
+ const _excluded$4 = ["keypadElement", "onFocus", "value"];
21387
21397
  class SimpleKeypadInput extends React.Component {
21388
21398
  constructor(...args) {
21389
21399
  super(...args);
@@ -21422,7 +21432,7 @@ class SimpleKeypadInput extends React.Component {
21422
21432
  onFocus,
21423
21433
  value
21424
21434
  } = _this$props,
21425
- rest = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
21435
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$4);
21426
21436
  return (
21427
21437
  /*#__PURE__*/
21428
21438
  // @ts-expect-error - TS2769 - No overload matches this call.
@@ -23253,14 +23263,14 @@ var Choice$1 = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.
23253
23263
  forwardedRef: ref
23254
23264
  })));
23255
23265
 
23256
- const _excluded$2 = ["showContent", "content", "forwardedRef"];
23266
+ const _excluded$3 = ["showContent", "content", "forwardedRef"];
23257
23267
  const ChoiceNoneAbove = function ChoiceNoneAbove(props) {
23258
23268
  const {
23259
23269
  showContent,
23260
23270
  content,
23261
23271
  forwardedRef
23262
23272
  } = props,
23263
- rest = _objectWithoutPropertiesLoose(props, _excluded$2);
23273
+ rest = _objectWithoutPropertiesLoose(props, _excluded$3);
23264
23274
  const choiceProps = _extends({}, rest, {
23265
23275
  content: showContent ? content :
23266
23276
  /*#__PURE__*/
@@ -28164,6 +28174,872 @@ Graph.defaultProps = {
28164
28174
  isMobile: false
28165
28175
  };
28166
28176
 
28177
+ const useInteractivePoint = (coords, snaps, range) => useMovablePoint(coords, {
28178
+ constrain: coord => constrain(coord, snaps, range),
28179
+ color: Color.blue
28180
+ });
28181
+
28182
+ /**
28183
+ * We do not want to call `onGraphChange` until the user
28184
+ * has interacted with the graph. See the test,
28185
+ * "should reject no interaction"
28186
+ */
28187
+ const useEffectAfterFirstRender = (fn, deps) => {
28188
+ const firstRender = useRef(true);
28189
+ useEffect(() => {
28190
+ if (firstRender.current) {
28191
+ firstRender.current = false;
28192
+ return;
28193
+ }
28194
+ fn();
28195
+ // eslint-disable-next-line react-hooks/exhaustive-deps
28196
+ }, [fn, ...deps]);
28197
+ };
28198
+ const snap = (val, step) => {
28199
+ const inverse = 1 / step;
28200
+ return Math.round(val * inverse) / inverse;
28201
+ };
28202
+ const clamp = (n, min, max) => Math.max(min, Math.min(max, n));
28203
+ const constrain = (coord, snapStep, range) => {
28204
+ const [x, y] = coord;
28205
+ const [xSnap, ySnap] = snapStep;
28206
+ const [[xMin, xMax], [yMin, yMax]] = range;
28207
+ return [clamp(snap(x, xSnap), xMin, xMax), clamp(snap(y, ySnap), yMin, yMax)];
28208
+ };
28209
+
28210
+ // same as pointsFromNormalized in interactive-graph.tsx
28211
+ const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
28212
+ // maps over [x, y]
28213
+ coords.map((coord, i) => {
28214
+ const axisRange = range[i];
28215
+ if (noSnap) {
28216
+ return axisRange[0] + (axisRange[1] - axisRange[0]) * coord;
28217
+ }
28218
+ const axisStep = step[i];
28219
+ const nSteps = Math.floor((axisRange[1] - axisRange[0]) / axisStep);
28220
+ const tick = Math.round(coord * nSteps);
28221
+ return axisRange[0] + axisStep * tick;
28222
+ }));
28223
+
28224
+ // same as normalizeCoords in interactive-graph.tsx
28225
+ const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
28226
+ const extent = ranges[i][1] - ranges[i][0];
28227
+ return (coord + ranges[i][1]) / extent;
28228
+ }));
28229
+
28230
+ const getSegmentCoords = props => {
28231
+ const coords = props.graph.coords;
28232
+ if (coords) {
28233
+ return coords;
28234
+ }
28235
+ const ys = n => {
28236
+ switch (n) {
28237
+ case 2:
28238
+ return [5, -5];
28239
+ case 3:
28240
+ return [5, 0, -5];
28241
+ case 4:
28242
+ return [6, 2, -2, -6];
28243
+ case 5:
28244
+ return [6, 3, 0, -3, -6];
28245
+ case 6:
28246
+ return [5, 3, 1, -1, -3, -5];
28247
+ default:
28248
+ return [5];
28249
+ }
28250
+ };
28251
+ return ys(props.graph.numSegments).map(y => {
28252
+ let segment = [[-5, y], [5, y]];
28253
+ segment = normalizeCoords(segment, props.range);
28254
+ segment = normalizePoints(props.range, props.step, segment);
28255
+ return segment;
28256
+ });
28257
+ };
28258
+ const updateSegmentsArray = (segments, index, segment) => segments.map((seg, i) => index === i ? segment : seg);
28259
+ const SegmentGraph = props => {
28260
+ const segments = getSegmentCoords(props);
28261
+ const handleChange = (i, segment) => props.onGraphChange(current => _extends({}, current, {
28262
+ coords: current.coords ? updateSegmentsArray(current.coords, i, segment) : updateSegmentsArray(segments, i, segment)
28263
+ }));
28264
+ return /*#__PURE__*/React.createElement(React.Fragment, null, segments.map((segment, i) => /*#__PURE__*/React.createElement(Segment, {
28265
+ key: i,
28266
+ i: i,
28267
+ segment: segment,
28268
+ snaps: props.snapStep,
28269
+ range: props.range,
28270
+ onChange: handleChange,
28271
+ "data-testid": "segment" + i
28272
+ })));
28273
+ };
28274
+ const Segment = props => {
28275
+ const [start, end] = props.segment;
28276
+ const {
28277
+ point: pt1,
28278
+ element: el1
28279
+ } = useInteractivePoint(start, props.snaps, props.range);
28280
+ const {
28281
+ point: pt2,
28282
+ element: el2
28283
+ } = useInteractivePoint(end, props.snaps, props.range);
28284
+ useEffectAfterFirstRender(() => props.onChange(props.i, [pt1, pt2]), [pt1, pt2]);
28285
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Line$4.Segment, {
28286
+ point1: pt1,
28287
+ point2: pt2
28288
+ }), el1, el2);
28289
+ };
28290
+
28291
+ const renderLineLabel = (n, [min, max]) => n !== -1 && n !== min && n !== max;
28292
+ const axisOptions = (props, index) => ({
28293
+ axis: props.markings === "graph",
28294
+ lines: props.step[index],
28295
+ subdivisions: props.step[index] / props.gridStep[index],
28296
+ labels: n => renderLineLabel(n, props.range[index]) ? n : ""
28297
+ });
28298
+ const Grid = props => props.markings === "none" ? null : /*#__PURE__*/React.createElement(Coordinates.Cartesian, {
28299
+ xAxis: axisOptions(props, 0),
28300
+ yAxis: axisOptions(props, 1)
28301
+ });
28302
+
28303
+ // Displays a stylized open eye: lint warnings are visible
28304
+ const VisibleIcon = () => /*#__PURE__*/React.createElement("svg", {
28305
+ width: "24",
28306
+ height: "24",
28307
+ viewBox: "0 0 24 24",
28308
+ className: css(styles$j.icon)
28309
+ }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
28310
+ id: "a",
28311
+ d: "M7.401 10.035c-1.424.748-2.599 1.905-3.544 " + "3.48a1 1 0 0 1-1.714-1.03C4.325 8.849 7.652 7 " + "12 7c4.348 0 7.675 1.848 9.857 5.486a1 1 0 0 " + "1-1.714 1.028c-.945-1.574-2.12-2.73-3.544-" + "3.48a5 5 0 1 1-9.198 0zM12 15a3 3 0 1 0 0-6 3 3 " + "0 0 0 0 6z"
28312
+ })), /*#__PURE__*/React.createElement("g", {
28313
+ fill: "none",
28314
+ fillRule: "evenodd"
28315
+ }, /*#__PURE__*/React.createElement("path", {
28316
+ fill: "none",
28317
+ d: "M0 0h24v24H0z"
28318
+ }), /*#__PURE__*/React.createElement("mask", {
28319
+ id: "b",
28320
+ fill: "#fff"
28321
+ }, /*#__PURE__*/React.createElement("use", {
28322
+ href: "#a"
28323
+ })), /*#__PURE__*/React.createElement("use", {
28324
+ fill: "#fff",
28325
+ fillRule: "nonzero",
28326
+ href: "#a"
28327
+ }), /*#__PURE__*/React.createElement("g", {
28328
+ fill: "#fff",
28329
+ mask: "url(#b)"
28330
+ }, /*#__PURE__*/React.createElement("path", {
28331
+ d: "M0 0h24v24H0z"
28332
+ }))));
28333
+
28334
+ // Displays a stylized eye with a line through it: I don't want to see lint
28335
+ const HiddenIcon = () => /*#__PURE__*/React.createElement("svg", {
28336
+ width: "24",
28337
+ height: "24",
28338
+ viewBox: "0 0 24 24",
28339
+ className: css(styles$j.icon)
28340
+ }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
28341
+ id: "a",
28342
+ d: "M8.794 7.38C9.791 7.127 10.86 7 12 7c4.348 0 " + "7.675 1.848 9.857 5.486a1 1 0 0 1-1.714 " + "1.028c-.945-1.574-2.12-2.73-3.544-3.48.258." + "604.401 1.268.401 1.966 0 1.02-.305 " + "1.967-.828 2.757l2.535 2.536a1 1 0 0 " + "1-1.414 1.414l-12-12a1 1 0 0 1 " + "1.414-1.414L8.794 7.38zm5.914 5.913a3 3 0 0 " + "0-4.001-4.001l4 4.001zM6.072 8.486l2.976 " + "2.976a3 3 0 0 0 3.49 3.49l1.579 1.58A5 5 0 " + "0 1 7.4 10.035c-1.424.747-2.599 1.904-3.544 " + "3.478a1 1 0 0 1-1.714-1.028c1.049-1.75 " + "2.363-3.085 3.929-4z"
28343
+ })), /*#__PURE__*/React.createElement("g", {
28344
+ fill: "none",
28345
+ fillRule: "evenodd"
28346
+ }, /*#__PURE__*/React.createElement("path", {
28347
+ fill: "none",
28348
+ d: "M0 0h24v24H0z"
28349
+ }), /*#__PURE__*/React.createElement("mask", {
28350
+ id: "b",
28351
+ fill: "#fff"
28352
+ }, /*#__PURE__*/React.createElement("use", {
28353
+ href: "#a"
28354
+ })), /*#__PURE__*/React.createElement("use", {
28355
+ fill: "#fff",
28356
+ fillRule: "nonzero",
28357
+ href: "#a"
28358
+ }), /*#__PURE__*/React.createElement("g", {
28359
+ fill: "#fff",
28360
+ mask: "url(#b)"
28361
+ }, /*#__PURE__*/React.createElement("path", {
28362
+ d: "M0 0h24v24H0z"
28363
+ }))));
28364
+ const HUD = ({
28365
+ message,
28366
+ enabled,
28367
+ onClick,
28368
+ fixedPosition: _fixedPosition = true
28369
+ }) => {
28370
+ let state;
28371
+ let icon;
28372
+ if (enabled) {
28373
+ state = styles$j.enabled;
28374
+ icon = /*#__PURE__*/React.createElement(VisibleIcon, null);
28375
+ } else {
28376
+ state = styles$j.disabled;
28377
+ icon = /*#__PURE__*/React.createElement(HiddenIcon, null);
28378
+ }
28379
+ return /*#__PURE__*/React.createElement("button", {
28380
+ className: css(styles$j.hud, _fixedPosition && styles$j.hudFixedPosition, state),
28381
+ onClick: e => {
28382
+ onClick();
28383
+ }
28384
+ }, icon, message);
28385
+ };
28386
+ const styles$j = StyleSheet.create({
28387
+ hud: {
28388
+ boxSizing: "border-box",
28389
+ height: 36,
28390
+ padding: "9px 16px",
28391
+ borderRadius: 18,
28392
+ fontFamily: boldFontFamily,
28393
+ fontSize: "15px",
28394
+ lineHeight: "18px",
28395
+ color: white$1,
28396
+ userSelect: "none",
28397
+ borderWidth: 0 // <button> gives us a border by default
28398
+ },
28399
+
28400
+ // We want to render the element in-place in our fixtures, so allow us to
28401
+ // render in the bottom corner in production and in-place in testing.
28402
+ hudFixedPosition: {
28403
+ bottom: 20,
28404
+ position: "fixed",
28405
+ right: 20,
28406
+ zIndex: 1
28407
+ },
28408
+ icon: {
28409
+ width: 24,
28410
+ height: 24,
28411
+ marginRight: 8,
28412
+ marginTop: -3,
28413
+ verticalAlign: "middle"
28414
+ },
28415
+ enabled: {
28416
+ backgroundColor: warningColor,
28417
+ ":hover": {
28418
+ backgroundColor: warningColorHover
28419
+ },
28420
+ ":active": {
28421
+ backgroundColor: warningColorActive
28422
+ }
28423
+ },
28424
+ disabled: {
28425
+ backgroundColor: gray76,
28426
+ ":hover": {
28427
+ backgroundColor: "#a1a5a9" // in between those two grays
28428
+ },
28429
+
28430
+ ":active": {
28431
+ backgroundColor: gray68
28432
+ }
28433
+ }
28434
+ });
28435
+
28436
+ const colors$1 = {
28437
+ grayLight: "#aaa",
28438
+ basicBorderColor: "#ccc",
28439
+ white: "#fff"
28440
+ };
28441
+ const triangleBeforeAfter = {
28442
+ borderBottom: "9px solid transparent",
28443
+ borderTop: "9px solid transparent",
28444
+ content: '" "',
28445
+ height: "0",
28446
+ position: "absolute",
28447
+ top: "0",
28448
+ width: "0"
28449
+ };
28450
+ const styles$i = StyleSheet.create({
28451
+ infoTip: {
28452
+ display: "inline-block",
28453
+ marginLeft: "5px",
28454
+ position: "relative"
28455
+ },
28456
+ infoTipContainer: {
28457
+ position: "absolute",
28458
+ top: "-12px",
28459
+ left: "22px",
28460
+ zIndex: 1000
28461
+ },
28462
+ infoTipTriangle: {
28463
+ height: "10px",
28464
+ left: "0",
28465
+ position: "absolute",
28466
+ top: "8px",
28467
+ width: "0",
28468
+ zIndex: 1,
28469
+ ":before": _extends({}, triangleBeforeAfter, {
28470
+ borderRight: "9px solid #bbb",
28471
+ right: "0"
28472
+ }),
28473
+ ":after": _extends({}, triangleBeforeAfter, {
28474
+ borderRight: `9px solid ${colors$1.white}`,
28475
+ right: "-1px"
28476
+ })
28477
+ },
28478
+ verticalShadow: {
28479
+ border: `1px solid ${colors$1.basicBorderColor}`,
28480
+ boxShadow: `0 1px 3px ${colors$1.basicBorderColor}`,
28481
+ borderBottom: `1px solid ${colors$1.grayLight}`
28482
+ },
28483
+ infoTipContentContainer: {
28484
+ background: colors$1.white,
28485
+ padding: "5px 10px",
28486
+ width: "240px"
28487
+ }
28488
+ });
28489
+ const questionMark = "data:image/png;base64," + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBB" + "ZG9iZSBJbWFnZVJlYWR5ccllPAAAA3NpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/" + "eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+" + "IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2Jl" + "IFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAg" + "ICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5" + "LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9" + "IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHht" + "bG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3Vy" + "Y2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHht" + "cE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo2N2M3NTAxYS04YmVlLTQ0M2Mt" + "YmRiNS04OGM2N2IxN2NhYzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUJCRTk4" + "Qjc4NjAwMTFFMzg3QUJDNEI4Mzk2QTRGQkQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5p" + "aWQ6OUJCRTk4QjY4NjAwMTFFMzg3QUJDNEI4Mzk2QTRGQkQiIHhtcDpDcmVhdG9yVG9v" + "bD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRG" + "cm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NGE5ZDI0OTMtODk1NC00OGFkLTlh" + "MTgtZDAwM2MwYWNjNDJlIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY3Yzc1MDFh" + "LThiZWUtNDQzYy1iZGI1LTg4YzY3YjE3Y2FjMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4g" + "PC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pqm89uYAAADM" + "SURBVHjaXJA9DoJAEIUH1M4TUHIFsCMGen9OwCGw1YRGW2ntKel0exsojHIBC0ouQAyU" + "viFDstmXfNmZeS+zm7XSNCXRFiRgJf0bXIHixpbhGdxBBJYC1w/xaA424MhNEATkui71" + "fU9KqfEU78UbD9PdbJRlOdae55GmhIP+1NV1TcMwkOM41DSNHvRtMhTHMRVFQW3b6mOL" + "gx99kue5GRp/gIOZuZGvNpTNwjD8oliANU+qqqKu6/TQBdymN57AHjzBT+B6Jx79BRgA" + "vc49kQA4yxgAAAAASUVORK5CYII=";
28490
+ class InfoTip$1 extends React.Component {
28491
+ constructor(...args) {
28492
+ super(...args);
28493
+ this.state = {
28494
+ hover: false
28495
+ };
28496
+ this.handleMouseEnter = () => {
28497
+ this.setState({
28498
+ hover: true
28499
+ });
28500
+ };
28501
+ this.handleMouseLeave = () => {
28502
+ this.setState({
28503
+ hover: false
28504
+ });
28505
+ };
28506
+ }
28507
+ render() {
28508
+ return /*#__PURE__*/React.createElement("div", {
28509
+ className: css(styles$i.infoTip)
28510
+ }, /*#__PURE__*/React.createElement("img", {
28511
+ alt: "",
28512
+ width: 10,
28513
+ height: 10,
28514
+ src: questionMark,
28515
+ onMouseEnter: this.handleMouseEnter,
28516
+ onMouseLeave: this.handleMouseLeave
28517
+ }), /*#__PURE__*/React.createElement("div", {
28518
+ className: css(styles$i.infoTipContainer),
28519
+ style: {
28520
+ display: this.state.hover ? "block" : "none"
28521
+ }
28522
+ }, /*#__PURE__*/React.createElement("div", {
28523
+ className: css(styles$i.infoTipTriangle)
28524
+ }), /*#__PURE__*/React.createElement("div", {
28525
+ className: css(styles$i.verticalShadow, styles$i.infoTipContentContainer)
28526
+ }, this.props.children)));
28527
+ }
28528
+ }
28529
+
28530
+ /**
28531
+ * A wrapper around info-tip/info-tip.jsx that can be rendered on the
28532
+ * server without causing a checksum mismatch on the client.
28533
+ * (RCSS generates classnames with a randomSuffix, which ensures that any
28534
+ * two sets of generated classnames will not match.)
28535
+ */
28536
+ class InfoTip extends React.Component {
28537
+ constructor(...args) {
28538
+ super(...args);
28539
+ this.state = {
28540
+ didMount: false
28541
+ };
28542
+ }
28543
+ componentDidMount() {
28544
+ /* eslint-disable react/no-did-mount-set-state */
28545
+ this.setState({
28546
+ didMount: true
28547
+ });
28548
+ /* eslint-enable react/no-did-mount-set-state */
28549
+ }
28550
+
28551
+ render() {
28552
+ if (this.state.didMount) {
28553
+ return /*#__PURE__*/React.createElement(InfoTip$1, this.props);
28554
+ }
28555
+ return /*#__PURE__*/React.createElement("div", null);
28556
+ }
28557
+ }
28558
+
28559
+ /**
28560
+ * MultiButtonGroup is an aesthetically pleasing group of buttons,
28561
+ * which allows multiple buttons to be selected at the same time.
28562
+ *
28563
+ * NOTE: This component is almost identical to ./button-group.jsx except that
28564
+ * this component allows multiple selection!
28565
+ */
28566
+ class MultiButtonGroup extends React.Component {
28567
+ constructor(...args) {
28568
+ super(...args);
28569
+ this.toggleSelect = newValue => {
28570
+ const values = (this.props.values || []).slice(0);
28571
+ const allowEmpty = this.props.allowEmpty;
28572
+ if (values.indexOf(newValue) >= 0 && (values.length > 1 || allowEmpty)) {
28573
+ // If the value is already selected, unselect it
28574
+ values.splice(values.indexOf(newValue), 1);
28575
+ } else {
28576
+ // Otherwise merge with other values and return
28577
+ if (values.indexOf(newValue) < 0) {
28578
+ values.push(newValue);
28579
+ }
28580
+ }
28581
+ this.props.onChange(values);
28582
+ };
28583
+ }
28584
+ focus() {
28585
+ var _ReactDOM$findDOMNode;
28586
+ // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element | Text'.
28587
+ (_ReactDOM$findDOMNode = ReactDOM.findDOMNode(this)) == null || _ReactDOM$findDOMNode.focus();
28588
+ return true;
28589
+ }
28590
+ render() {
28591
+ const values = this.props.values || [];
28592
+ const buttons = this.props.buttons.map((button, i) => {
28593
+ const selected = values.indexOf(button.value) >= 0;
28594
+ return /*#__PURE__*/React.createElement("button", {
28595
+ title: button.title,
28596
+ type: "button",
28597
+ id: "" + i,
28598
+ key: "" + i,
28599
+ ref: "button" + i,
28600
+ className: css(styles$h.buttonStyle, selected && styles$h.selectedStyle),
28601
+ onClick: () => this.toggleSelect(button.value)
28602
+ }, button.content || "" + button.value);
28603
+ });
28604
+ const outerStyle = {
28605
+ display: "inline-block"
28606
+ };
28607
+ return /*#__PURE__*/React.createElement("div", {
28608
+ style: outerStyle
28609
+ }, buttons);
28610
+ }
28611
+ }
28612
+ MultiButtonGroup.defaultProps = {
28613
+ values: [],
28614
+ allowEmpty: true
28615
+ };
28616
+ const styles$h = StyleSheet.create({
28617
+ buttonStyle: {
28618
+ backgroundColor: "white",
28619
+ border: "1px solid #ccc",
28620
+ borderLeft: "0",
28621
+ cursor: "pointer",
28622
+ margin: "0",
28623
+ padding: "5px 10px",
28624
+ position: "relative",
28625
+ // for hover
28626
+
28627
+ ":first-child": {
28628
+ borderLeft: "1px solid #ccc",
28629
+ borderTopLeftRadius: "3px",
28630
+ borderBottomLeftRadius: "3px"
28631
+ },
28632
+ ":last-child": {
28633
+ borderRight: "1px solid #ccc",
28634
+ borderTopRightRadius: "3px",
28635
+ borderBottomRightRadius: "3px"
28636
+ },
28637
+ ":hover": {
28638
+ backgroundColor: "#ccc"
28639
+ },
28640
+ ":focus": {
28641
+ zIndex: 2
28642
+ }
28643
+ },
28644
+ selectedStyle: {
28645
+ backgroundColor: "#ddd"
28646
+ }
28647
+ });
28648
+
28649
+ const _excluded$2 = ["onFormatChange", "checkValidity", "useArrowKeys"];
28650
+ const {
28651
+ firstNumericalParse,
28652
+ captureScratchpadTouchStart
28653
+ } = Util;
28654
+ const toNumericString = KhanMath.toNumericString;
28655
+ const getNumericFormat = KhanMath.getNumericFormat;
28656
+
28657
+ /* An input box that accepts only numeric strings
28658
+ *
28659
+ * Calls onChange(value, format) for valid numbers.
28660
+ * Reverts to the current value onBlur or on [ENTER],
28661
+ * but maintains the format (i.e. 3/2, 1 1/2, 150%)
28662
+ * Accepts empty input and sends it to onChange as null
28663
+ * if no numeric placeholder is set.
28664
+ * If given a checkValidity function, will turn
28665
+ * the background/outline red when invalid
28666
+ * If useArrowKeys is set to true, up/down arrows will
28667
+ * increment/decrement integers
28668
+ * Optionally takes a size ("mini", "small", "normal")
28669
+ */
28670
+ class NumberInput extends React.Component {
28671
+ constructor(...args) {
28672
+ super(...args);
28673
+ this.state = {
28674
+ format: this.props.format
28675
+ };
28676
+ /* Return the current "value" of this input
28677
+ * If empty, it returns the placeholder (if it is a number) or null
28678
+ */
28679
+ this.getValue = () => {
28680
+ return this.parseInputValue(
28681
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
28682
+ ReactDOM__default.findDOMNode(this.refs.input).value // eslint-disable-line react/no-string-refs
28683
+ );
28684
+ };
28685
+ /* Return the current string value of this input */
28686
+ this.getStringValue = () => {
28687
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
28688
+ return ReactDOM__default.findDOMNode(this.refs.input).value.toString(); // eslint-disable-line react/no-string-refs
28689
+ };
28690
+ this.parseInputValue = value => {
28691
+ if (value === "") {
28692
+ const placeholder = this.props.placeholder;
28693
+ return _.isFinite(placeholder) ? +placeholder : null;
28694
+ }
28695
+ const result = firstNumericalParse(value);
28696
+ return _.isFinite(result) ? result : this.props.value;
28697
+ };
28698
+ /* Set text input focus to this input */
28699
+ this.focus = () => {
28700
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
28701
+ ReactDOM__default.findDOMNode(this.refs.input).focus(); // eslint-disable-line react/no-string-refs
28702
+ this._handleFocus();
28703
+ };
28704
+ this.blur = () => {
28705
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'blur' does not exist on type 'Element | Text'.
28706
+ ReactDOM__default.findDOMNode(this.refs.input).blur(); // eslint-disable-line react/no-string-refs
28707
+ this._handleBlur();
28708
+ };
28709
+ this.setSelectionRange = (selectionStart, selectionEnd) => {
28710
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'setSelectionRange' does not exist on type 'Element | Text'.
28711
+ ReactDOM__default.findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
28712
+ };
28713
+ this.getSelectionStart = () => {
28714
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'selectionStart' does not exist on type 'Element | Text'.
28715
+ return ReactDOM__default.findDOMNode(this).selectionStart;
28716
+ };
28717
+ this.getSelectionEnd = () => {
28718
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'selectionEnd' does not exist on type 'Element | Text'.
28719
+ return ReactDOM__default.findDOMNode(this).selectionEnd;
28720
+ };
28721
+ this._checkValidity = value => {
28722
+ if (value == null) {
28723
+ return true;
28724
+ }
28725
+ const val = firstNumericalParse(value);
28726
+ const checkValidity = this.props.checkValidity;
28727
+ return _.isFinite(val) && checkValidity(val);
28728
+ };
28729
+ this._handleChange = e => {
28730
+ const text = e.target.value;
28731
+ const value = this.parseInputValue(text);
28732
+ const format = getNumericFormat(text);
28733
+ this.props.onChange(value);
28734
+ if (format) {
28735
+ this.props.onFormatChange(value, format);
28736
+ this.setState({
28737
+ format: format
28738
+ });
28739
+ }
28740
+ };
28741
+ this._handleFocus = () => {
28742
+ if (this.props.onFocus) {
28743
+ this.props.onFocus();
28744
+ }
28745
+ };
28746
+ this._handleBlur = e => {
28747
+ // Only continue on blur or "enter"
28748
+ // @ts-expect-error - TS2339 - Property 'keyCode' does not exist on type 'KeyboardEvent<Element> | FocusEvent<Element, Element>'.
28749
+ if (e && e.type === "keypress" && e.keyCode !== 13) {
28750
+ return;
28751
+ }
28752
+ this._setValue(this.props.value, this.state.format);
28753
+ if (this.props.onBlur) {
28754
+ this.props.onBlur();
28755
+ }
28756
+ };
28757
+ this._onKeyDown = e => {
28758
+ if (this.props.onKeyDown) {
28759
+ this.props.onKeyDown(e);
28760
+ }
28761
+ if (!this.props.useArrowKeys || !_.contains(["ArrowUp", "ArrowDown"], e.key)) {
28762
+ return;
28763
+ }
28764
+ let val = this.getValue();
28765
+ if (val !== Math.floor(val)) {
28766
+ return; // bail if not an integer
28767
+ }
28768
+
28769
+ if (e.key === "ArrowUp") {
28770
+ val = val + 1;
28771
+ } else if (e.key === "ArrowDown") {
28772
+ val = val - 1;
28773
+ }
28774
+ if (this._checkValidity(val)) {
28775
+ this.props.onChange(val);
28776
+ }
28777
+ };
28778
+ this._setValue = (val, format) => {
28779
+ // eslint-disable-next-line react/no-string-refs
28780
+ // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'val' does not exist on type 'JQueryStatic'.
28781
+ $(ReactDOM__default.findDOMNode(this.refs.input)).val(toNumericString(val, format));
28782
+ };
28783
+ }
28784
+ render() {
28785
+ let classes = classNames({
28786
+ "number-input": true,
28787
+ "invalid-input": !this._checkValidity(this.props.value),
28788
+ mini: this.props.size === "mini",
28789
+ small: this.props.size === "small",
28790
+ normal: this.props.size === "normal"
28791
+ });
28792
+ if (this.props.className != null) {
28793
+ classes = classes + " " + this.props.className;
28794
+ }
28795
+ const _this$props = this.props,
28796
+ restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$2);
28797
+ return /*#__PURE__*/React.createElement("input", _extends({}, restProps, {
28798
+ className: classes,
28799
+ type: "text"
28800
+ // eslint-disable-next-line react/no-string-refs
28801
+ ,
28802
+ ref: "input",
28803
+ onChange: this._handleChange,
28804
+ onFocus: this._handleFocus,
28805
+ onBlur: this._handleBlur,
28806
+ onKeyPress: this._handleBlur,
28807
+ onKeyDown: this._onKeyDown
28808
+ // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
28809
+ ,
28810
+ onTouchStart: captureScratchpadTouchStart,
28811
+ defaultValue: toNumericString(this.props.value, this.state.format),
28812
+ value: undefined
28813
+ }));
28814
+ }
28815
+ componentDidUpdate(prevProps) {
28816
+ if (!number.equal(this.getValue(), this.props.value)) {
28817
+ this._setValue(this.props.value, this.state.format);
28818
+ }
28819
+ }
28820
+ }
28821
+ NumberInput.propTypes = {
28822
+ value: PropTypes.number,
28823
+ format: PropTypes.string,
28824
+ placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
28825
+ onChange: PropTypes.func.isRequired,
28826
+ onFormatChange: PropTypes.func,
28827
+ checkValidity: PropTypes.func,
28828
+ size: PropTypes.string,
28829
+ label: PropTypes.oneOf(["put your labels outside your inputs!"])
28830
+ };
28831
+ NumberInput.defaultProps = {
28832
+ value: null,
28833
+ placeholder: null,
28834
+ format: null,
28835
+ onFormatChange: () => null,
28836
+ checkValidity: () => true,
28837
+ useArrowKeys: false
28838
+ };
28839
+
28840
+ /* eslint-disable @babel/no-invalid-this */
28841
+
28842
+ /* A checkbox that syncs its value to props using the
28843
+ * renderer's onChange method, and gets the prop name
28844
+ * dynamically from its props list
28845
+ */
28846
+ const PropCheckBox = createReactClass({
28847
+ displayName: "PropCheckBox",
28848
+ propTypes: {
28849
+ labelAlignment: PropTypes.oneOf(["left", "right"])
28850
+ },
28851
+ DEFAULT_PROPS: {
28852
+ label: null,
28853
+ onChange: null,
28854
+ labelAlignment: "left"
28855
+ },
28856
+ getDefaultProps: function () {
28857
+ return this.DEFAULT_PROPS;
28858
+ },
28859
+ propName: function () {
28860
+ const propName = _.find(_.keys(this.props), function (localPropName) {
28861
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
28862
+ return !_.has(this.DEFAULT_PROPS, localPropName);
28863
+ }, this);
28864
+ if (!propName) {
28865
+ throw new PerseusError("Attempted to create a PropCheckBox with no prop!", Errors.InvalidInput);
28866
+ }
28867
+ return propName;
28868
+ },
28869
+ _labelAlignLeft: function () {
28870
+ return this.props.labelAlignment === "left";
28871
+ },
28872
+ render: function () {
28873
+ const propName = this.propName();
28874
+ return /*#__PURE__*/React.createElement("label", null, this._labelAlignLeft() && this.props.label, /*#__PURE__*/React.createElement("input", {
28875
+ type: "checkbox",
28876
+ checked: this.props[propName],
28877
+ onChange: this.toggle
28878
+ }), !this._labelAlignLeft() && this.props.label);
28879
+ },
28880
+ toggle: function () {
28881
+ const propName = this.propName();
28882
+ const changes = {};
28883
+ changes[propName] = !this.props[propName];
28884
+ this.props.onChange(changes);
28885
+ }
28886
+ });
28887
+
28888
+ const truth = () => true;
28889
+
28890
+ /* A minor abstraction on top of NumberInput for ranges
28891
+ *
28892
+ */
28893
+ class RangeInput extends React.Component {
28894
+ constructor(...args) {
28895
+ super(...args);
28896
+ this.onChange = (i, newVal) => {
28897
+ const value = this.props.value;
28898
+ if (i === 0) {
28899
+ this.props.onChange([newVal, value[1]]);
28900
+ } else {
28901
+ this.props.onChange([value[0], newVal]);
28902
+ }
28903
+ };
28904
+ }
28905
+ render() {
28906
+ const value = this.props.value;
28907
+ const checkValidity = this.props.checkValidity || truth;
28908
+ return /*#__PURE__*/React.createElement("div", {
28909
+ className: "range-input"
28910
+ }, /*#__PURE__*/React.createElement(NumberInput, _extends({}, this.props, {
28911
+ value: value[0],
28912
+ checkValidity: val => checkValidity([val, value[1]])
28913
+ // eslint-disable-next-line react/jsx-no-bind
28914
+ ,
28915
+ onChange: this.onChange.bind(this, 0),
28916
+ placeholder: this.props.placeholder[0]
28917
+ })), /*#__PURE__*/React.createElement(NumberInput, _extends({}, this.props, {
28918
+ value: value[1],
28919
+ checkValidity: val => checkValidity([value[0], val])
28920
+ // eslint-disable-next-line react/jsx-no-bind
28921
+ ,
28922
+ onChange: this.onChange.bind(this, 1),
28923
+ placeholder: this.props.placeholder[1]
28924
+ })));
28925
+ }
28926
+ }
28927
+ RangeInput.propTypes = {
28928
+ value: PropTypes.array.isRequired,
28929
+ onChange: PropTypes.func.isRequired,
28930
+ placeholder: PropTypes.array,
28931
+ checkValidity: PropTypes.func
28932
+ };
28933
+ RangeInput.defaultProps = {
28934
+ placeholder: [null, null]
28935
+ };
28936
+
28937
+ var components = /*#__PURE__*/Object.freeze({
28938
+ __proto__: null,
28939
+ ButtonGroup: ButtonGroup,
28940
+ Graph: Graph,
28941
+ Graphie: Graphie,
28942
+ HUD: HUD,
28943
+ Icon: Icon,
28944
+ InfoTip: InfoTip,
28945
+ InlineIcon: InlineIcon,
28946
+ MathInput: MathInput,
28947
+ MultiButtonGroup: MultiButtonGroup,
28948
+ NumberInput: NumberInput,
28949
+ PropCheckBox: PropCheckBox,
28950
+ RangeInput: RangeInput,
28951
+ SvgImage: SvgImage,
28952
+ TextInput: TextInput,
28953
+ TextListEditor: TextListEditor
28954
+ });
28955
+
28956
+ /**
28957
+ * If a graphie URL is provided in `backgroundImage`, will return the rendered graphie background.
28958
+ * Otherwise, returns `null`.
28959
+ */
28960
+ const getLegacyGrid = (box, backgroundImage) => {
28961
+ const {
28962
+ url,
28963
+ width,
28964
+ height
28965
+ } = backgroundImage != null ? backgroundImage : {};
28966
+ if (url && typeof url === "string") {
28967
+ const scale = box[0] / interactiveSizes$1.defaultBoxSize;
28968
+ return /*#__PURE__*/React.createElement(View, {
28969
+ style: {
28970
+ position: "absolute",
28971
+ bottom: 0,
28972
+ left: 0
28973
+ }
28974
+ }, /*#__PURE__*/React.createElement(context$1.Consumer, null, ({
28975
+ setAssetStatus
28976
+ }) => /*#__PURE__*/React.createElement(SvgImage, {
28977
+ src: url,
28978
+ width: width,
28979
+ height: height,
28980
+ scale: scale,
28981
+ responsive: false,
28982
+ setAssetStatus: setAssetStatus,
28983
+ alt: ""
28984
+ })));
28985
+ }
28986
+ return null;
28987
+ };
28988
+
28989
+ const _excluded$1 = ["graph"];
28990
+ const renderGraph = props => {
28991
+ const {
28992
+ graph
28993
+ } = props,
28994
+ rest = _objectWithoutPropertiesLoose(props, _excluded$1);
28995
+ switch (graph.type) {
28996
+ case "segment":
28997
+ return /*#__PURE__*/React.createElement(SegmentGraph, _extends({}, rest, {
28998
+ graph: graph,
28999
+ onGraphChange: props.onGraphChange
29000
+ }));
29001
+ }
29002
+ };
29003
+ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
29004
+ var _props$backgroundImag, _props$backgroundImag2, _props$backgroundImag3, _props$backgroundImag4;
29005
+ // Storing the gradable state in a ref so that it can be updated without
29006
+ // causing a re-render.
29007
+ const graphRef = React.useRef(props.graph);
29008
+ const handleGraphUpdate = callback => {
29009
+ graphRef.current = callback(graphRef.current);
29010
+ };
29011
+ React.useImperativeHandle(ref, () => ({
29012
+ getUserInput: () => graphRef.current
29013
+ }));
29014
+ const [width, height] = getInteractiveBoxFromSizeClass(props.containerSizeClass);
29015
+ const legacyGrid = getLegacyGrid([width, height], props.backgroundImage);
29016
+ return /*#__PURE__*/React.createElement(View, {
29017
+ style: {
29018
+ width: (_props$backgroundImag = (_props$backgroundImag2 = props.backgroundImage) == null ? void 0 : _props$backgroundImag2.width) != null ? _props$backgroundImag : width,
29019
+ height: (_props$backgroundImag3 = (_props$backgroundImag4 = props.backgroundImage) == null ? void 0 : _props$backgroundImag4.height) != null ? _props$backgroundImag3 : height,
29020
+ position: "relative"
29021
+ }
29022
+ }, legacyGrid, /*#__PURE__*/React.createElement(View, {
29023
+ style: {
29024
+ position: "absolute",
29025
+ bottom: 0,
29026
+ left: 0
29027
+ }
29028
+ }, /*#__PURE__*/React.createElement(Mafs, {
29029
+ viewBox: {
29030
+ x: props.range[0],
29031
+ y: props.range[1],
29032
+ padding: 0
29033
+ },
29034
+ pan: false,
29035
+ zoom: false,
29036
+ width: width,
29037
+ height: height
29038
+ }, !!legacyGrid && /*#__PURE__*/React.createElement(Grid, props), renderGraph(_extends({}, props, {
29039
+ onGraphChange: handleGraphUpdate
29040
+ })))));
29041
+ });
29042
+
28167
29043
  const {
28168
29044
  DeprecationMixin
28169
29045
  } = Util;
@@ -28241,6 +29117,7 @@ class InteractiveGraph extends React.Component {
28241
29117
  this.sinusoid = void 0;
28242
29118
  this.trashCan = void 0;
28243
29119
  this.vertHairline = void 0;
29120
+ this.mafsRef = /*#__PURE__*/React.createRef();
28244
29121
  this.state = {
28245
29122
  shouldShowInstructions: _getShouldShowInstructions(this.props)
28246
29123
  };
@@ -29320,11 +30197,10 @@ class InteractiveGraph extends React.Component {
29320
30197
  });
29321
30198
  };
29322
30199
  this.getUserInput = () => {
29323
- return InteractiveGraph.getUserInputFromProps(this.props);
29324
- };
29325
- this.simpleValidate = rubric => {
29326
- return InteractiveGraph.validate(this.getUserInput(), rubric, this);
30200
+ var _this$mafsRef$current, _this$mafsRef$current2;
30201
+ return (_this$mafsRef$current = (_this$mafsRef$current2 = this.mafsRef.current) == null || _this$mafsRef$current2.getUserInput == null ? void 0 : _this$mafsRef$current2.getUserInput()) != null ? _this$mafsRef$current : InteractiveGraph.getUserInputFromProps(this.props);
29327
30202
  };
30203
+ this.simpleValidate = rubric => InteractiveGraph.validate(this.getUserInput(), rubric, this);
29328
30204
  this.focus = $.noop;
29329
30205
  }
29330
30206
  // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
@@ -29332,9 +30208,11 @@ class InteractiveGraph extends React.Component {
29332
30208
  DeprecationMixin.UNSAFE_componentWillMount.call(this);
29333
30209
  }
29334
30210
  componentDidMount() {
29335
- // eslint-disable-next-line react/no-string-refs
29336
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
29337
- this.setGraphie(this.refs.graph.graphie());
30211
+ if (this.refs.graph) {
30212
+ // eslint-disable-next-line react/no-string-refs
30213
+ // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
30214
+ this.setGraphie(this.refs.graph.graphie());
30215
+ }
29338
30216
  }
29339
30217
  UNSAFE_componentWillReceiveProps(nextProps) {
29340
30218
  var _this$props$backgroun, _nextProps$background;
@@ -29374,9 +30252,19 @@ class InteractiveGraph extends React.Component {
29374
30252
  areSegmentGraphsEqual(prevGraph, currentGraph) {
29375
30253
  return prevGraph.numSegments !== currentGraph.numSegments;
29376
30254
  }
30255
+ // const segment: boolean = useFeatureIsOn("mafs-segment-graph");
30256
+ // <Renderer apiOptions={{flags: {mafs: {segment}}}}
29377
30257
  render() {
30258
+ var _this$props$apiOption;
30259
+ // Mafs shim
30260
+ if ((_this$props$apiOption = this.props.apiOptions) != null && (_this$props$apiOption = _this$props$apiOption.flags) != null && (_this$props$apiOption = _this$props$apiOption["mafs"]) != null && _this$props$apiOption[this.props.graph.type]) {
30261
+ return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, this.props, {
30262
+ ref: this.mafsRef
30263
+ }));
30264
+ }
29378
30265
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
29379
30266
  let instructions;
30267
+ // isClickToAddPoints() only applies to points and polygons
29380
30268
  if (this.isClickToAddPoints() && this.state.shouldShowInstructions) {
29381
30269
  if (this.props.graph.type === "point") {
29382
30270
  instructions = i18n._("Click to add points");
@@ -30679,7 +31567,7 @@ var globalConstants = /*#__PURE__*/Object.freeze({
30679
31567
  pureLgMax: pureLgMax
30680
31568
  });
30681
31569
 
30682
- const colors$1 = {
31570
+ const colors = {
30683
31571
  white: "#FFFFFF",
30684
31572
  gray98: "#FAFAFA",
30685
31573
  gray97: "#F6F7F7",
@@ -30739,7 +31627,7 @@ const colors$1 = {
30739
31627
  };
30740
31628
  const borderRadius = 4;
30741
31629
  const breadcrumbStyles = {
30742
- color: colors$1.breadcrumbs,
31630
+ color: colors.breadcrumbs,
30743
31631
  fontFamily: "inherit",
30744
31632
  fontSize: 15,
30745
31633
  fontWeight: "bold",
@@ -30809,14 +31697,14 @@ const typography = {
30809
31697
  },
30810
31698
  // For callouts
30811
31699
  smallHeading: {
30812
- color: colors$1.gray25,
31700
+ color: colors.gray25,
30813
31701
  fontFamily: "inherit",
30814
31702
  fontSize: 17,
30815
31703
  fontWeight: "bold",
30816
31704
  lineHeight: "23px"
30817
31705
  },
30818
31706
  smallHeadingMobile: {
30819
- color: colors$1.gray25,
31707
+ color: colors.gray25,
30820
31708
  fontFamily: "inherit",
30821
31709
  fontSize: 15,
30822
31710
  fontWeight: "bold",
@@ -30824,7 +31712,7 @@ const typography = {
30824
31712
  },
30825
31713
  // Accent headings
30826
31714
  accentHeading: {
30827
- color: colors$1.gray68,
31715
+ color: colors.gray68,
30828
31716
  fontFamily: "inherit",
30829
31717
  fontSize: 15,
30830
31718
  fontWeight: "bold",
@@ -30930,7 +31818,7 @@ const typography = {
30930
31818
  // HACK(kevinb): temporary wonder blocks styles for header only
30931
31819
  // TODO(kevinb): use components from wonder-blocks-typography instead
30932
31820
  wonderBlocksTitle: {
30933
- color: colors$1.white,
31821
+ color: colors.white,
30934
31822
  fontWeight: 900,
30935
31823
  // "Black" weight
30936
31824
  fontSize: 28,
@@ -30952,7 +31840,7 @@ const typography = {
30952
31840
 
30953
31841
  var globalStyles = /*#__PURE__*/Object.freeze({
30954
31842
  __proto__: null,
30955
- colors: colors$1,
31843
+ colors: colors,
30956
31844
  borderRadius: borderRadius,
30957
31845
  fonts: fonts,
30958
31846
  typography: typography
@@ -31014,7 +31902,7 @@ const HideAnswersToggle = props => {
31014
31902
  const switchId = idFactory.get("hide-answers-toggle");
31015
31903
  const labelId = idFactory.get("hide-answers-label");
31016
31904
  return /*#__PURE__*/React.createElement(View, {
31017
- style: styles$j.switchWrapper
31905
+ style: styles$g.switchWrapper
31018
31906
  }, /*#__PURE__*/React.createElement(Switch, {
31019
31907
  id: switchId,
31020
31908
  checked: props.areAnswersHidden,
@@ -31026,7 +31914,7 @@ const HideAnswersToggle = props => {
31026
31914
  tag: "label"
31027
31915
  }, strings.hideAnswersToggleLabel));
31028
31916
  };
31029
- const styles$j = StyleSheet.create({
31917
+ const styles$g = StyleSheet.create({
31030
31918
  switchWrapper: {
31031
31919
  display: "flex",
31032
31920
  flexDirection: "row",
@@ -31079,13 +31967,13 @@ const AnswerPill = props => {
31079
31967
  id: idFactory.get("perseus-label-image-widget-answer-pill"),
31080
31968
  onClick: correct ? undefined : onClick,
31081
31969
  ref: ref,
31082
- style: [style, popperStyle, styles$i.pill, correct && styles$i.correct, incorrect && styles$i.incorrect, (focused || hovered) && BringToFront]
31970
+ style: [style, popperStyle, styles$f.pill, correct && styles$f.correct, incorrect && styles$f.incorrect, (focused || hovered) && BringToFront]
31083
31971
  }, /*#__PURE__*/React.createElement(Renderer, {
31084
31972
  content: answerString,
31085
31973
  inline: true
31086
31974
  })));
31087
31975
  };
31088
- const styles$i = StyleSheet.create({
31976
+ const styles$f = StyleSheet.create({
31089
31977
  correct: {
31090
31978
  // WB green darkened by 18%
31091
31979
  backgroundColor: "#00880b"
@@ -31138,23 +32026,23 @@ class Marker extends React.Component {
31138
32026
  icon: iconNull
31139
32027
  };
31140
32028
  if (showCorrectness) {
31141
- iconStyles = [styles$h.markerGraded, showCorrectness === "correct" ? styles$h.markerCorrect : styles$h.markerIncorrect, isOpen && styles$h.markerSelected];
32029
+ iconStyles = [styles$e.markerGraded, showCorrectness === "correct" ? styles$e.markerCorrect : styles$e.markerIncorrect, isOpen && styles$e.markerSelected];
31142
32030
  args = _extends({}, args, {
31143
32031
  icon: showCorrectness === "correct" ? iconCheck : iconMinus
31144
32032
  });
31145
32033
  } else if (selectedAnswers && selectedAnswers.length > 0) {
31146
- iconStyles = [styles$h.markerFilled, isOpen && styles$h.markerSelected];
32034
+ iconStyles = [styles$e.markerFilled, isOpen && styles$e.markerSelected];
31147
32035
  } else if (isOpen) {
31148
- iconStyles = [styles$h.markerSelected];
32036
+ iconStyles = [styles$e.markerSelected];
31149
32037
  args = _extends({}, args, {
31150
32038
  icon: iconChevronDown,
31151
32039
  size: 8
31152
32040
  });
31153
32041
  } else if (showPulsate) {
31154
- iconStyles = [styles$h.markerPulsateBase, shouldReduceMotion() ? showPulsate && styles$h.markerUnfilledPulsateOnce : showPulsate && styles$h.markerUnfilledPulsateInfinite];
32042
+ iconStyles = [styles$e.markerPulsateBase, shouldReduceMotion() ? showPulsate && styles$e.markerUnfilledPulsateOnce : showPulsate && styles$e.markerUnfilledPulsateInfinite];
31155
32043
  }
31156
32044
  return /*#__PURE__*/React.createElement(View, {
31157
- style: [styles$h.markerIcon, iconStyles],
32045
+ style: [styles$e.markerIcon, iconStyles],
31158
32046
  ref: node => this._icon = node
31159
32047
  }, /*#__PURE__*/React.createElement(Icon, args));
31160
32048
  }
@@ -31176,7 +32064,7 @@ class Marker extends React.Component {
31176
32064
  const markerDisabled = showCorrectness === "correct";
31177
32065
  const active = hovered || focused;
31178
32066
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
31179
- style: [styles$h.marker, active && !markerDisabled && styles$h.markerActive]
32067
+ style: [styles$e.marker, active && !markerDisabled && styles$e.markerActive]
31180
32068
  // already translated in: /widgets/graded-group.tsx
31181
32069
  ,
31182
32070
  "aria-label": markerDisabled ? i18n._("Correct!") : label
@@ -31194,7 +32082,7 @@ class Marker extends React.Component {
31194
32082
  Marker.defaultProps = {
31195
32083
  selected: []
31196
32084
  };
31197
- const styles$h = StyleSheet.create({
32085
+ const styles$e = StyleSheet.create({
31198
32086
  marker: {
31199
32087
  position: "absolute",
31200
32088
  backgroundColor: Color.white,
@@ -31764,13 +32652,13 @@ class LabelImage extends React.Component {
31764
32652
  const promptString = isMobile ? multipleAnswers ? i18n._("Tap each dot on the image to select all answers that apply.") : i18n._("Tap each dot on the image to select an answer.") : multipleAnswers ? i18n._("Click each dot on the image to select all answers that apply.") : i18n._("Click each dot on the image to select an answer.");
31765
32653
  const choicesString = i18n._("Choices:");
31766
32654
  return /*#__PURE__*/React.createElement("div", {
31767
- className: classNames("perseus-label-image-widget-instructions", css(styles$g.instructions))
32655
+ className: classNames("perseus-label-image-widget-instructions", css(styles$d.instructions))
31768
32656
  }, /*#__PURE__*/React.createElement("div", {
31769
- className: css(styles$g.instructionsCaption)
32657
+ className: css(styles$d.instructionsCaption)
31770
32658
  }, promptString, " ", !hideChoices && choicesString), !hideChoices && /*#__PURE__*/React.createElement("div", {
31771
- className: css(styles$g.instructionsChoices)
32659
+ className: css(styles$d.instructionsChoices)
31772
32660
  }, choices.map((choice, index) => /*#__PURE__*/React.createElement("div", {
31773
- className: css(styles$g.instructionsChoice),
32661
+ className: css(styles$d.instructionsChoice),
31774
32662
  key: index
31775
32663
  }, /*#__PURE__*/React.createElement(Renderer, {
31776
32664
  content: choice
@@ -31787,20 +32675,20 @@ class LabelImage extends React.Component {
31787
32675
  activeMarkerIndex
31788
32676
  } = this.state;
31789
32677
  return /*#__PURE__*/React.createElement("div", null, this.renderInstructions(), /*#__PURE__*/React.createElement("div", {
31790
- className: css(styles$g.markersCanvas),
32678
+ className: css(styles$d.markersCanvas),
31791
32679
  style: {
31792
32680
  maxWidth: imageWidth,
31793
32681
  maxHeight: imageHeight
31794
32682
  }
31795
32683
  }, /*#__PURE__*/React.createElement("div", {
31796
- className: css(styles$g.imageContainer,
32684
+ className: css(styles$d.imageContainer,
31797
32685
  // Ensure image interaction is disabled while answer
31798
32686
  // choices popup is visible. This addresses specific
31799
32687
  // case where user may click on question image to
31800
32688
  // dismiss the popup. If the image is larger in size
31801
32689
  // than its rendered in the widget, this would
31802
32690
  // result in a zoom of the image.
31803
- activeMarkerIndex !== -1 && styles$g.imageInteractionDisabled)
32691
+ activeMarkerIndex !== -1 && styles$d.imageInteractionDisabled)
31804
32692
  }, /*#__PURE__*/React.createElement(context$1.Consumer, null, ({
31805
32693
  setAssetStatus
31806
32694
  }) => /*#__PURE__*/React.createElement(SvgImage, {
@@ -31824,7 +32712,7 @@ class LabelImage extends React.Component {
31824
32712
  }));
31825
32713
  }
31826
32714
  }
31827
- const styles$g = StyleSheet.create({
32715
+ const styles$d = StyleSheet.create({
31828
32716
  instructions: {
31829
32717
  paddingBottom: 16
31830
32718
  },
@@ -32181,7 +33069,7 @@ class Placeholder extends React.Component {
32181
33069
  const {
32182
33070
  layout
32183
33071
  } = this.props;
32184
- const className = css(styles$f.card, styles$f.placeholder, layout === Layout.HORIZONTAL && styles$f.horizontalCard);
33072
+ const className = css(styles$c.card, styles$c.placeholder, layout === Layout.HORIZONTAL && styles$c.horizontalCard);
32185
33073
  const style = {
32186
33074
  width: this.props.width,
32187
33075
  height: this.props.height
@@ -32364,7 +33252,7 @@ class Draggable extends React.Component {
32364
33252
  // We need to keep backwards compatbility with rules specified directly
32365
33253
  // in CSS. Hence the hacky tacking on of manual classNames.
32366
33254
  // See sortable.less for details.
32367
- let className = css(styles$f.card, styles$f.draggable, layout === Layout.HORIZONTAL && styles$f.horizontalCard, layout === Layout.VERTICAL && styles$f.verticalCard, type === ItemState.DRAGGING && styles$f.dragging, type === ItemState.DISABLED && styles$f.disabled, !includePadding && styles$f.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
33255
+ let className = css(styles$c.card, styles$c.draggable, layout === Layout.HORIZONTAL && styles$c.horizontalCard, layout === Layout.VERTICAL && styles$c.verticalCard, type === ItemState.DRAGGING && styles$c.dragging, type === ItemState.DISABLED && styles$c.disabled, !includePadding && styles$c.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
32368
33256
  if (!includePadding) {
32369
33257
  className += " perseus-sortable-draggable-unpadded";
32370
33258
  }
@@ -32571,7 +33459,7 @@ class Sortable extends React.Component {
32571
33459
  } = this.props;
32572
33460
  // We need to keep backwards compatbility with rules specified directly
32573
33461
  // in CSS. See sortable.less for details.
32574
- const className = css(styles$f.sortable) + " perseus-sortable";
33462
+ const className = css(styles$c.sortable) + " perseus-sortable";
32575
33463
  const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === Layout.VERTICAL;
32576
33464
  const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === Layout.HORIZONTAL;
32577
33465
  _.each(this.state.items, function (item, i, items) {
@@ -32813,7 +33701,7 @@ Sortable.defaultProps = {
32813
33701
  linterContext: PerseusLinter.linterContextDefault,
32814
33702
  waitForTexRendererToLoad: true
32815
33703
  };
32816
- const styles$f = StyleSheet.create({
33704
+ const styles$c = StyleSheet.create({
32817
33705
  sortable: {
32818
33706
  boxSizing: "border-box",
32819
33707
  float: "left",
@@ -32975,23 +33863,23 @@ class Matcher extends React.Component {
32975
33863
  };
32976
33864
  const cellMarginPx = this.props.apiOptions.isMobile ? 8 : 5;
32977
33865
  return /*#__PURE__*/React.createElement("table", {
32978
- className: css(styles$e.widget) + " " + HACKY_CSS_CLASSNAME
33866
+ className: css(styles$b.widget) + " " + HACKY_CSS_CLASSNAME
32979
33867
  }, /*#__PURE__*/React.createElement("tbody", null, showLabels && /*#__PURE__*/React.createElement("tr", {
32980
- className: css(styles$e.row)
33868
+ className: css(styles$b.row)
32981
33869
  }, /*#__PURE__*/React.createElement("th", {
32982
- className: css(styles$e.column, styles$e.columnLabel)
33870
+ className: css(styles$b.column, styles$b.columnLabel)
32983
33871
  }, /*#__PURE__*/React.createElement(Renderer, {
32984
33872
  content: this.props.labels[0] || "...",
32985
33873
  linterContext: this.props.linterContext
32986
33874
  })), /*#__PURE__*/React.createElement("th", {
32987
- className: css(styles$e.column, styles$e.columnRight, styles$e.columnLabel)
33875
+ className: css(styles$b.column, styles$b.columnRight, styles$b.columnLabel)
32988
33876
  }, /*#__PURE__*/React.createElement(Renderer, {
32989
33877
  content: this.props.labels[1] || "...",
32990
33878
  linterContext: this.props.linterContext
32991
33879
  }))), /*#__PURE__*/React.createElement("tr", {
32992
- className: css(styles$e.row)
33880
+ className: css(styles$b.row)
32993
33881
  }, /*#__PURE__*/React.createElement("td", {
32994
- className: css(styles$e.column)
33882
+ className: css(styles$b.column)
32995
33883
  }, /*#__PURE__*/React.createElement(Sortable, {
32996
33884
  options: left,
32997
33885
  layout: Layout.VERTICAL,
@@ -33006,7 +33894,7 @@ class Matcher extends React.Component {
33006
33894
  ,
33007
33895
  ref: "left"
33008
33896
  })), /*#__PURE__*/React.createElement("td", {
33009
- className: css(styles$e.column, styles$e.columnRight)
33897
+ className: css(styles$b.column, styles$b.columnRight)
33010
33898
  }, /*#__PURE__*/React.createElement(Sortable
33011
33899
  // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
33012
33900
  , {
@@ -33059,7 +33947,7 @@ _.extend(Matcher, {
33059
33947
  });
33060
33948
  const padding = 5;
33061
33949
  const border = "1px solid #444";
33062
- const styles$e = StyleSheet.create({
33950
+ const styles$b = StyleSheet.create({
33063
33951
  widget: {
33064
33952
  paddingTop: padding,
33065
33953
  maxWidth: "100%",
@@ -33092,197 +33980,6 @@ var Matcher$1 = {
33092
33980
  isLintable: true
33093
33981
  };
33094
33982
 
33095
- const _excluded$1 = ["onFormatChange", "checkValidity", "useArrowKeys"];
33096
- const {
33097
- firstNumericalParse,
33098
- captureScratchpadTouchStart
33099
- } = Util;
33100
- const toNumericString = KhanMath.toNumericString;
33101
- const getNumericFormat = KhanMath.getNumericFormat;
33102
-
33103
- /* An input box that accepts only numeric strings
33104
- *
33105
- * Calls onChange(value, format) for valid numbers.
33106
- * Reverts to the current value onBlur or on [ENTER],
33107
- * but maintains the format (i.e. 3/2, 1 1/2, 150%)
33108
- * Accepts empty input and sends it to onChange as null
33109
- * if no numeric placeholder is set.
33110
- * If given a checkValidity function, will turn
33111
- * the background/outline red when invalid
33112
- * If useArrowKeys is set to true, up/down arrows will
33113
- * increment/decrement integers
33114
- * Optionally takes a size ("mini", "small", "normal")
33115
- */
33116
- class NumberInput extends React.Component {
33117
- constructor(...args) {
33118
- super(...args);
33119
- this.state = {
33120
- format: this.props.format
33121
- };
33122
- /* Return the current "value" of this input
33123
- * If empty, it returns the placeholder (if it is a number) or null
33124
- */
33125
- this.getValue = () => {
33126
- return this.parseInputValue(
33127
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
33128
- ReactDOM__default.findDOMNode(this.refs.input).value // eslint-disable-line react/no-string-refs
33129
- );
33130
- };
33131
- /* Return the current string value of this input */
33132
- this.getStringValue = () => {
33133
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
33134
- return ReactDOM__default.findDOMNode(this.refs.input).value.toString(); // eslint-disable-line react/no-string-refs
33135
- };
33136
- this.parseInputValue = value => {
33137
- if (value === "") {
33138
- const placeholder = this.props.placeholder;
33139
- return _.isFinite(placeholder) ? +placeholder : null;
33140
- }
33141
- const result = firstNumericalParse(value);
33142
- return _.isFinite(result) ? result : this.props.value;
33143
- };
33144
- /* Set text input focus to this input */
33145
- this.focus = () => {
33146
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
33147
- ReactDOM__default.findDOMNode(this.refs.input).focus(); // eslint-disable-line react/no-string-refs
33148
- this._handleFocus();
33149
- };
33150
- this.blur = () => {
33151
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'blur' does not exist on type 'Element | Text'.
33152
- ReactDOM__default.findDOMNode(this.refs.input).blur(); // eslint-disable-line react/no-string-refs
33153
- this._handleBlur();
33154
- };
33155
- this.setSelectionRange = (selectionStart, selectionEnd) => {
33156
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'setSelectionRange' does not exist on type 'Element | Text'.
33157
- ReactDOM__default.findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
33158
- };
33159
- this.getSelectionStart = () => {
33160
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'selectionStart' does not exist on type 'Element | Text'.
33161
- return ReactDOM__default.findDOMNode(this).selectionStart;
33162
- };
33163
- this.getSelectionEnd = () => {
33164
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'selectionEnd' does not exist on type 'Element | Text'.
33165
- return ReactDOM__default.findDOMNode(this).selectionEnd;
33166
- };
33167
- this._checkValidity = value => {
33168
- if (value == null) {
33169
- return true;
33170
- }
33171
- const val = firstNumericalParse(value);
33172
- const checkValidity = this.props.checkValidity;
33173
- return _.isFinite(val) && checkValidity(val);
33174
- };
33175
- this._handleChange = e => {
33176
- const text = e.target.value;
33177
- const value = this.parseInputValue(text);
33178
- const format = getNumericFormat(text);
33179
- this.props.onChange(value);
33180
- if (format) {
33181
- this.props.onFormatChange(value, format);
33182
- this.setState({
33183
- format: format
33184
- });
33185
- }
33186
- };
33187
- this._handleFocus = () => {
33188
- if (this.props.onFocus) {
33189
- this.props.onFocus();
33190
- }
33191
- };
33192
- this._handleBlur = e => {
33193
- // Only continue on blur or "enter"
33194
- // @ts-expect-error - TS2339 - Property 'keyCode' does not exist on type 'KeyboardEvent<Element> | FocusEvent<Element, Element>'.
33195
- if (e && e.type === "keypress" && e.keyCode !== 13) {
33196
- return;
33197
- }
33198
- this._setValue(this.props.value, this.state.format);
33199
- if (this.props.onBlur) {
33200
- this.props.onBlur();
33201
- }
33202
- };
33203
- this._onKeyDown = e => {
33204
- if (this.props.onKeyDown) {
33205
- this.props.onKeyDown(e);
33206
- }
33207
- if (!this.props.useArrowKeys || !_.contains(["ArrowUp", "ArrowDown"], e.key)) {
33208
- return;
33209
- }
33210
- let val = this.getValue();
33211
- if (val !== Math.floor(val)) {
33212
- return; // bail if not an integer
33213
- }
33214
-
33215
- if (e.key === "ArrowUp") {
33216
- val = val + 1;
33217
- } else if (e.key === "ArrowDown") {
33218
- val = val - 1;
33219
- }
33220
- if (this._checkValidity(val)) {
33221
- this.props.onChange(val);
33222
- }
33223
- };
33224
- this._setValue = (val, format) => {
33225
- // eslint-disable-next-line react/no-string-refs
33226
- // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'val' does not exist on type 'JQueryStatic'.
33227
- $(ReactDOM__default.findDOMNode(this.refs.input)).val(toNumericString(val, format));
33228
- };
33229
- }
33230
- render() {
33231
- let classes = classNames({
33232
- "number-input": true,
33233
- "invalid-input": !this._checkValidity(this.props.value),
33234
- mini: this.props.size === "mini",
33235
- small: this.props.size === "small",
33236
- normal: this.props.size === "normal"
33237
- });
33238
- if (this.props.className != null) {
33239
- classes = classes + " " + this.props.className;
33240
- }
33241
- const _this$props = this.props,
33242
- restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
33243
- return /*#__PURE__*/React.createElement("input", _extends({}, restProps, {
33244
- className: classes,
33245
- type: "text"
33246
- // eslint-disable-next-line react/no-string-refs
33247
- ,
33248
- ref: "input",
33249
- onChange: this._handleChange,
33250
- onFocus: this._handleFocus,
33251
- onBlur: this._handleBlur,
33252
- onKeyPress: this._handleBlur,
33253
- onKeyDown: this._onKeyDown
33254
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
33255
- ,
33256
- onTouchStart: captureScratchpadTouchStart,
33257
- defaultValue: toNumericString(this.props.value, this.state.format),
33258
- value: undefined
33259
- }));
33260
- }
33261
- componentDidUpdate(prevProps) {
33262
- if (!number.equal(this.getValue(), this.props.value)) {
33263
- this._setValue(this.props.value, this.state.format);
33264
- }
33265
- }
33266
- }
33267
- NumberInput.propTypes = {
33268
- value: PropTypes.number,
33269
- format: PropTypes.string,
33270
- placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
33271
- onChange: PropTypes.func.isRequired,
33272
- onFormatChange: PropTypes.func,
33273
- checkValidity: PropTypes.func,
33274
- size: PropTypes.string,
33275
- label: PropTypes.oneOf(["put your labels outside your inputs!"])
33276
- };
33277
- NumberInput.defaultProps = {
33278
- value: null,
33279
- placeholder: null,
33280
- format: null,
33281
- onFormatChange: () => null,
33282
- checkValidity: () => true,
33283
- useArrowKeys: false
33284
- };
33285
-
33286
33983
  // Type imports
33287
33984
 
33288
33985
  const {
@@ -33926,9 +34623,9 @@ function drawText(ctx, item) {
33926
34623
  if (item.value === null) {
33927
34624
  return;
33928
34625
  }
33929
- ctx.fillStyle = styles$d.fgColor;
33930
- ctx.font = styles$d.font;
33931
- ctx.fillText(item.value, item.pos[0] - styles$d.fontSizePx / 2 + 1, item.pos[1] + styles$d.fontSizePx / 2);
34626
+ ctx.fillStyle = styles$a.fgColor;
34627
+ ctx.font = styles$a.font;
34628
+ ctx.fillText(item.value, item.pos[0] - styles$a.fontSizePx / 2 + 1, item.pos[1] + styles$a.fontSizePx / 2);
33932
34629
  }
33933
34630
 
33934
34631
  /**
@@ -33949,16 +34646,16 @@ function drawText(ctx, item) {
33949
34646
  function drawDoubleLine(ctx, item) {
33950
34647
  // Outer line that forms both of the bond lines.
33951
34648
  let path = new Path2D();
33952
- ctx.lineWidth = 5 * styles$d.lineWidth;
33953
- ctx.strokeStyle = styles$d.fgColor;
34649
+ ctx.lineWidth = 5 * styles$a.lineWidth;
34650
+ ctx.strokeStyle = styles$a.fgColor;
33954
34651
  path.moveTo(item.startPos[0], item.startPos[1]);
33955
34652
  path.lineTo(item.endPos[0], item.endPos[1]);
33956
34653
  ctx.stroke(path);
33957
34654
 
33958
34655
  // Inner white line that separates the two bond lines.
33959
34656
  path = new Path2D();
33960
- ctx.lineWidth = 3 * styles$d.lineWidth;
33961
- ctx.strokeStyle = styles$d.bgColor;
34657
+ ctx.lineWidth = 3 * styles$a.lineWidth;
34658
+ ctx.strokeStyle = styles$a.bgColor;
33962
34659
  path.moveTo(item.startPos[0], item.startPos[1]);
33963
34660
  path.lineTo(item.endPos[0], item.endPos[1]);
33964
34661
  ctx.stroke(path);
@@ -33985,16 +34682,16 @@ function drawTripleLine(ctx, item) {
33985
34682
 
33986
34683
  // Outer line that will form the two outer bond lines.
33987
34684
  let path = new Path2D();
33988
- ctx.lineWidth = 7 * styles$d.lineWidth;
33989
- ctx.strokeStyle = styles$d.fgColor;
34685
+ ctx.lineWidth = 7 * styles$a.lineWidth;
34686
+ ctx.strokeStyle = styles$a.fgColor;
33990
34687
  path.moveTo(item.startPos[0], item.startPos[1]);
33991
34688
  path.lineTo(item.endPos[0], item.endPos[1]);
33992
34689
  ctx.stroke(path);
33993
34690
 
33994
34691
  // Middle white line that separates the bonds
33995
34692
  path = new Path2D();
33996
- ctx.lineWidth = 5 * styles$d.lineWidth;
33997
- ctx.strokeStyle = styles$d.bgColor;
34693
+ ctx.lineWidth = 5 * styles$a.lineWidth;
34694
+ ctx.strokeStyle = styles$a.bgColor;
33998
34695
  path.moveTo(item.startPos[0], item.startPos[1]);
33999
34696
  path.lineTo(item.endPos[0], item.endPos[1]);
34000
34697
  ctx.stroke(path);
@@ -34016,8 +34713,8 @@ function drawTripleLine(ctx, item) {
34016
34713
  */
34017
34714
  function drawLine(ctx, item) {
34018
34715
  const path = new Path2D();
34019
- ctx.lineWidth = styles$d.lineWidth;
34020
- ctx.strokeStyle = styles$d.fgColor;
34716
+ ctx.lineWidth = styles$a.lineWidth;
34717
+ ctx.strokeStyle = styles$a.fgColor;
34021
34718
  path.moveTo(item.startPos[0], item.startPos[1]);
34022
34719
  path.lineTo(item.endPos[0], item.endPos[1]);
34023
34720
  ctx.stroke(path);
@@ -34069,7 +34766,7 @@ function compareElements(item0, item1) {
34069
34766
  function draw(ctx, items) {
34070
34767
  items.sort(compareElements).forEach(drawItem(ctx));
34071
34768
  }
34072
- const styles$d = {
34769
+ const styles$a = {
34073
34770
  bgColor: "rgb(255, 255, 255)",
34074
34771
  fgColor: "rgb(0, 0, 0)",
34075
34772
  fontSizePx: 12,
@@ -36631,7 +37328,7 @@ class HighlightRenderer extends React.PureComponent {
36631
37328
  const rects = this.state.cachedHighlightRects;
36632
37329
  return /*#__PURE__*/React.createElement("div", null, rects.map((rect, index) => /*#__PURE__*/React.createElement("div", {
36633
37330
  key: index,
36634
- className: css(styles$c.highlightRect),
37331
+ className: css(styles$9.highlightRect),
36635
37332
  style: {
36636
37333
  // NOTE(mdr): We apply `position: absolute` here
36637
37334
  // rather than in Aphrodite styles, because
@@ -36650,7 +37347,7 @@ class HighlightRenderer extends React.PureComponent {
36650
37347
  })));
36651
37348
  }
36652
37349
  }
36653
- const styles$c = StyleSheet.create({
37350
+ const styles$9 = StyleSheet.create({
36654
37351
  highlightRect: {
36655
37352
  background: "#fffabe" // highlighter yellow :)
36656
37353
  }
@@ -36731,7 +37428,7 @@ class HighlightTooltip extends React.PureComponent {
36731
37428
  // TooltipContent wouldn't let me overwrite
36732
37429
  // user-select and onClick
36733
37430
  const content = /*#__PURE__*/React.createElement("div", {
36734
- className: css(styles$b.tooltipLabel),
37431
+ className: css(styles$8.tooltipLabel),
36735
37432
  onClick: this.props.onClick
36736
37433
  }, this.props.label);
36737
37434
  const style = {
@@ -36751,7 +37448,7 @@ class HighlightTooltip extends React.PureComponent {
36751
37448
  }, /*#__PURE__*/React.createElement("div", null)));
36752
37449
  }
36753
37450
  }
36754
- const styles$b = StyleSheet.create({
37451
+ const styles$8 = StyleSheet.create({
36755
37452
  tooltipLabel: {
36756
37453
  // Unlike most tooltip contents, we don't want this label to be
36757
37454
  // selectable. Selecting it is not only unhelpful, but dangerous: if
@@ -37303,7 +38000,7 @@ class HighlightableContent extends React.PureComponent {
37303
38000
  // starting point: https://phabricator.khanacademy.org/D35623?id=170698
37304
38001
  const buildHighlight = r => this._buildHighlight(r);
37305
38002
  return /*#__PURE__*/React.createElement("div", {
37306
- className: css(styles$a.container),
38003
+ className: css(styles$7.container),
37307
38004
  ref: container => this._container = container
37308
38005
  }, /*#__PURE__*/React.createElement("div", null, this.props.enabled && this._container && this._content && /*#__PURE__*/React.createElement(HighlightingUI, {
37309
38006
  buildHighlight: buildHighlight,
@@ -37324,14 +38021,14 @@ class HighlightableContent extends React.PureComponent {
37324
38021
  onAddHighlight: this._handleAddHighlight,
37325
38022
  onRemoveHighlight: this._handleRemoveHighlight
37326
38023
  })), /*#__PURE__*/React.createElement("div", {
37327
- className: css(styles$a.content),
38024
+ className: css(styles$7.content),
37328
38025
  ref: content => this._content = content
37329
38026
  }, /*#__PURE__*/React.createElement(WordIndexer, {
37330
38027
  onWordsUpdate: this._handleWordsUpdate
37331
38028
  }, this.props.children)));
37332
38029
  }
37333
38030
  }
37334
- const styles$a = StyleSheet.create({
38031
+ const styles$7 = StyleSheet.create({
37335
38032
  // We render content with z-index 1, and highlights with z-index 0, so that
37336
38033
  // content will receive the browser's native pointer events. (Highlights
37337
38034
  // have their own magic code to manage hover detection, despite being
@@ -37731,7 +38428,7 @@ class LineHeightMeasurer extends React.Component {
37731
38428
  }
37732
38429
  render() {
37733
38430
  return /*#__PURE__*/React.createElement("div", {
37734
- className: css(styles$9.measurer),
38431
+ className: css(styles$6.measurer),
37735
38432
  "aria-hidden": "true"
37736
38433
  }, /*#__PURE__*/React.createElement("div", {
37737
38434
  ref: ref => this._line = ref,
@@ -37739,7 +38436,7 @@ class LineHeightMeasurer extends React.Component {
37739
38436
  }, "Line Height Measurement"));
37740
38437
  }
37741
38438
  }
37742
- const styles$9 = StyleSheet.create({
38439
+ const styles$6 = StyleSheet.create({
37743
38440
  measurer: {
37744
38441
  position: "absolute",
37745
38442
  top: 0,
@@ -39051,7 +39748,7 @@ class PythonProgram extends React.Component {
39051
39748
  // creator "went wild".
39052
39749
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
39053
39750
  return /*#__PURE__*/React.createElement(View, {
39054
- style: styles$8.container
39751
+ style: styles$5.container
39055
39752
  }, /*#__PURE__*/React.createElement("iframe", {
39056
39753
  sandbox: sandboxOptions,
39057
39754
  src: url,
@@ -39063,7 +39760,7 @@ class PythonProgram extends React.Component {
39063
39760
  PythonProgram.defaultProps = {
39064
39761
  height: 400
39065
39762
  };
39066
- const styles$8 = StyleSheet.create({
39763
+ const styles$5 = StyleSheet.create({
39067
39764
  container: {
39068
39765
  margin: "auto",
39069
39766
  width: "100%"
@@ -39303,129 +40000,6 @@ var Sequence$1 = {
39303
40000
  isLintable: true
39304
40001
  };
39305
40002
 
39306
- const colors = {
39307
- grayLight: "#aaa",
39308
- basicBorderColor: "#ccc",
39309
- white: "#fff"
39310
- };
39311
- const triangleBeforeAfter = {
39312
- borderBottom: "9px solid transparent",
39313
- borderTop: "9px solid transparent",
39314
- content: '" "',
39315
- height: "0",
39316
- position: "absolute",
39317
- top: "0",
39318
- width: "0"
39319
- };
39320
- const styles$7 = StyleSheet.create({
39321
- infoTip: {
39322
- display: "inline-block",
39323
- marginLeft: "5px",
39324
- position: "relative"
39325
- },
39326
- infoTipContainer: {
39327
- position: "absolute",
39328
- top: "-12px",
39329
- left: "22px",
39330
- zIndex: 1000
39331
- },
39332
- infoTipTriangle: {
39333
- height: "10px",
39334
- left: "0",
39335
- position: "absolute",
39336
- top: "8px",
39337
- width: "0",
39338
- zIndex: 1,
39339
- ":before": _extends({}, triangleBeforeAfter, {
39340
- borderRight: "9px solid #bbb",
39341
- right: "0"
39342
- }),
39343
- ":after": _extends({}, triangleBeforeAfter, {
39344
- borderRight: `9px solid ${colors.white}`,
39345
- right: "-1px"
39346
- })
39347
- },
39348
- verticalShadow: {
39349
- border: `1px solid ${colors.basicBorderColor}`,
39350
- boxShadow: `0 1px 3px ${colors.basicBorderColor}`,
39351
- borderBottom: `1px solid ${colors.grayLight}`
39352
- },
39353
- infoTipContentContainer: {
39354
- background: colors.white,
39355
- padding: "5px 10px",
39356
- width: "240px"
39357
- }
39358
- });
39359
- const questionMark = "data:image/png;base64," + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBB" + "ZG9iZSBJbWFnZVJlYWR5ccllPAAAA3NpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/" + "eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+" + "IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2Jl" + "IFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAg" + "ICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5" + "LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9" + "IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHht" + "bG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3Vy" + "Y2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHht" + "cE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo2N2M3NTAxYS04YmVlLTQ0M2Mt" + "YmRiNS04OGM2N2IxN2NhYzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUJCRTk4" + "Qjc4NjAwMTFFMzg3QUJDNEI4Mzk2QTRGQkQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5p" + "aWQ6OUJCRTk4QjY4NjAwMTFFMzg3QUJDNEI4Mzk2QTRGQkQiIHhtcDpDcmVhdG9yVG9v" + "bD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRG" + "cm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NGE5ZDI0OTMtODk1NC00OGFkLTlh" + "MTgtZDAwM2MwYWNjNDJlIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY3Yzc1MDFh" + "LThiZWUtNDQzYy1iZGI1LTg4YzY3YjE3Y2FjMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4g" + "PC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pqm89uYAAADM" + "SURBVHjaXJA9DoJAEIUH1M4TUHIFsCMGen9OwCGw1YRGW2ntKel0exsojHIBC0ouQAyU" + "viFDstmXfNmZeS+zm7XSNCXRFiRgJf0bXIHixpbhGdxBBJYC1w/xaA424MhNEATkui71" + "fU9KqfEU78UbD9PdbJRlOdae55GmhIP+1NV1TcMwkOM41DSNHvRtMhTHMRVFQW3b6mOL" + "gx99kue5GRp/gIOZuZGvNpTNwjD8oliANU+qqqKu6/TQBdymN57AHjzBT+B6Jx79BRgA" + "vc49kQA4yxgAAAAASUVORK5CYII=";
39360
- class InfoTip$1 extends React.Component {
39361
- constructor(...args) {
39362
- super(...args);
39363
- this.state = {
39364
- hover: false
39365
- };
39366
- this.handleMouseEnter = () => {
39367
- this.setState({
39368
- hover: true
39369
- });
39370
- };
39371
- this.handleMouseLeave = () => {
39372
- this.setState({
39373
- hover: false
39374
- });
39375
- };
39376
- }
39377
- render() {
39378
- return /*#__PURE__*/React.createElement("div", {
39379
- className: css(styles$7.infoTip)
39380
- }, /*#__PURE__*/React.createElement("img", {
39381
- alt: "",
39382
- width: 10,
39383
- height: 10,
39384
- src: questionMark,
39385
- onMouseEnter: this.handleMouseEnter,
39386
- onMouseLeave: this.handleMouseLeave
39387
- }), /*#__PURE__*/React.createElement("div", {
39388
- className: css(styles$7.infoTipContainer),
39389
- style: {
39390
- display: this.state.hover ? "block" : "none"
39391
- }
39392
- }, /*#__PURE__*/React.createElement("div", {
39393
- className: css(styles$7.infoTipTriangle)
39394
- }), /*#__PURE__*/React.createElement("div", {
39395
- className: css(styles$7.verticalShadow, styles$7.infoTipContentContainer)
39396
- }, this.props.children)));
39397
- }
39398
- }
39399
-
39400
- /**
39401
- * A wrapper around info-tip/info-tip.jsx that can be rendered on the
39402
- * server without causing a checksum mismatch on the client.
39403
- * (RCSS generates classnames with a randomSuffix, which ensures that any
39404
- * two sets of generated classnames will not match.)
39405
- */
39406
- class InfoTip extends React.Component {
39407
- constructor(...args) {
39408
- super(...args);
39409
- this.state = {
39410
- didMount: false
39411
- };
39412
- }
39413
- componentDidMount() {
39414
- /* eslint-disable react/no-did-mount-set-state */
39415
- this.setState({
39416
- didMount: true
39417
- });
39418
- /* eslint-enable react/no-did-mount-set-state */
39419
- }
39420
-
39421
- render() {
39422
- if (this.state.didMount) {
39423
- return /*#__PURE__*/React.createElement(InfoTip$1, this.props);
39424
- }
39425
- return /*#__PURE__*/React.createElement("div", null);
39426
- }
39427
- }
39428
-
39429
40003
  const {
39430
40004
  assert: assert$1
39431
40005
  } = InteractiveUtil;
@@ -41380,7 +41954,7 @@ const VideoTranscriptLink = props => {
41380
41954
  var _result$data;
41381
41955
  const video = (_result$data = result.data) == null ? void 0 : _result$data.video;
41382
41956
  return /*#__PURE__*/React.createElement(View, {
41383
- style: styles$6.transcriptLink
41957
+ style: styles$4.transcriptLink
41384
41958
  }, /*#__PURE__*/React.createElement(Text$1, null, video == null ? void 0 : video.title), /*#__PURE__*/React.createElement(Strut, {
41385
41959
  size: 10
41386
41960
  }), /*#__PURE__*/React.createElement(Link, {
@@ -41397,7 +41971,7 @@ const VideoTranscriptLink = props => {
41397
41971
  return /*#__PURE__*/React.createElement(View, null, i18n._("Something went wrong."));
41398
41972
  }
41399
41973
  };
41400
- const styles$6 = StyleSheet.create({
41974
+ const styles$4 = StyleSheet.create({
41401
41975
  transcriptLink: {
41402
41976
  flexDirection: "row",
41403
41977
  width: "100%",
@@ -41550,7 +42124,7 @@ const init = function init(options) {
41550
42124
 
41551
42125
  // This file is processed by a Rollup plugin (replace) to inject the production
41552
42126
  const libName = "@khanacademy/perseus";
41553
- const libVersion = "19.6.2";
42127
+ const libVersion = "19.7.0";
41554
42128
  addLibraryVersionToPerseusDebug(libName, libVersion);
41555
42129
 
41556
42130
  const apiVersion = {
@@ -41760,7 +42334,7 @@ class HintRenderer extends React.Component {
41760
42334
  const {
41761
42335
  isMobile
41762
42336
  } = apiOptions;
41763
- const classNames$1 = classNames("hint", !isMobile && "perseus-hint-renderer", isMobile && css(styles$5.newHint), isMobile && lastRendered && css(styles$5.lastRenderedNewHint), lastHint && "last-hint", lastRendered && "last-rendered", className);
42337
+ const classNames$1 = classNames("hint", !isMobile && "perseus-hint-renderer", isMobile && css(styles$3.newHint), isMobile && lastRendered && css(styles$3.lastRenderedNewHint), lastHint && "last-hint", lastRendered && "last-rendered", className);
41764
42338
 
41765
42339
  // TODO(charlie): Allowing `customKeypad` would require that we
41766
42340
  // extend `ItemRenderer` to support nested inputs in the `HintsRenderer`.
@@ -41803,7 +42377,7 @@ class HintRenderer extends React.Component {
41803
42377
  HintRenderer.defaultProps = {
41804
42378
  linterContext: PerseusLinter.linterContextDefault
41805
42379
  };
41806
- const styles$5 = StyleSheet.create({
42380
+ const styles$3 = StyleSheet.create({
41807
42381
  newHint: {
41808
42382
  marginBottom: 1.5 * baseUnitPx,
41809
42383
  borderLeftColor: gray97,
@@ -41976,15 +42550,15 @@ class HintsRenderer extends React.Component {
41976
42550
  } = apiOptions;
41977
42551
  const showGetAnotherHint = getAnotherHint && hintsVisible > 0 && hintsVisible < this.props.hints.length;
41978
42552
  const hintRatioCopy = `(${hintsVisible}/${this.props.hints.length})`;
41979
- const classNames$1 = classNames(this.props.className, apiOptions.isMobile && hintsVisible > 0 && css(styles$4.mobileHintStylesHintsRenderer));
42553
+ const classNames$1 = classNames(this.props.className, apiOptions.isMobile && hintsVisible > 0 && css(styles$2.mobileHintStylesHintsRenderer));
41980
42554
  return /*#__PURE__*/React.createElement("div", {
41981
42555
  className: classNames$1
41982
42556
  }, apiOptions.isMobile && hintsVisible > 0 && /*#__PURE__*/React.createElement("div", {
41983
- className: css(styles$4.mobileHintStylesHintTitle, sharedStyles.responsiveLabel)
42557
+ className: css(styles$2.mobileHintStylesHintTitle, sharedStyles.responsiveLabel)
41984
42558
  }, i18n._("Hints")), hints, showGetAnotherHint && /*#__PURE__*/React.createElement("button", {
41985
42559
  // @ts-expect-error - TS2322 - Type '{ children: Element[]; rel: string; className: string; onClick: (evt: MouseEvent<HTMLButtonElement, MouseEvent>) => void; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
41986
42560
  rel: "button",
41987
- className: css(styles$4.linkButton, styles$4.getAnotherHintButton, apiOptions.isMobile && styles$4.mobileHintStylesGetAnotherHintButton),
42561
+ className: css(styles$2.linkButton, styles$2.getAnotherHintButton, apiOptions.isMobile && styles$2.mobileHintStylesGetAnotherHintButton),
41988
42562
  onClick: evt => {
41989
42563
  evt.preventDefault();
41990
42564
  evt.stopPropagation();
@@ -41993,9 +42567,9 @@ class HintsRenderer extends React.Component {
41993
42567
  }
41994
42568
  }
41995
42569
  }, /*#__PURE__*/React.createElement("span", {
41996
- className: css(styles$4.plusText, apiOptions.isMobile && styles$4.mobileHintStylesPlusText)
42570
+ className: css(styles$2.plusText, apiOptions.isMobile && styles$2.mobileHintStylesPlusText)
41997
42571
  }, "+"), /*#__PURE__*/React.createElement("span", {
41998
- className: css(styles$4.getAnotherHintText)
42572
+ className: css(styles$2.getAnotherHintText)
41999
42573
  }, i18n._("Get another hint"), " ", hintRatioCopy)));
42000
42574
  }
42001
42575
  }
@@ -42003,7 +42577,7 @@ HintsRenderer.defaultProps = {
42003
42577
  linterContext: PerseusLinter.linterContextDefault
42004
42578
  };
42005
42579
  const hintIndentation = baseUnitPx + hintBorderWidth;
42006
- const styles$4 = StyleSheet.create({
42580
+ const styles$2 = StyleSheet.create({
42007
42581
  // eslint-disable-next-line react-native/no-unused-styles
42008
42582
  rendererMargins: {
42009
42583
  marginTop: baseUnitPx
@@ -42410,7 +42984,7 @@ class ServerItemRenderer extends React.Component {
42410
42984
  // mobile hint styles, which are flush to the left.
42411
42985
  // NOTE(charlie): We may still want to apply this
42412
42986
  // padding for desktop exercises.
42413
- apiOptions.isMobile ? undefined : css(styles$3.hintsContainer)
42987
+ apiOptions.isMobile ? undefined : css(styles$1.hintsContainer)
42414
42988
  }, hintsRenderer)));
42415
42989
  }
42416
42990
  }
@@ -42420,7 +42994,7 @@ ServerItemRenderer.defaultProps = {
42420
42994
  linterContext: PerseusLinter.linterContextDefault,
42421
42995
  onRendered: isRendered => {}
42422
42996
  };
42423
- const styles$3 = StyleSheet.create({
42997
+ const styles$1 = StyleSheet.create({
42424
42998
  hintsContainer: {
42425
42999
  marginLeft: 50
42426
43000
  }
@@ -42554,345 +43128,6 @@ ElementContainer.defaultProps = {
42554
43128
  title: "More"
42555
43129
  };
42556
43130
 
42557
- // Displays a stylized open eye: lint warnings are visible
42558
- const VisibleIcon = () => /*#__PURE__*/React.createElement("svg", {
42559
- width: "24",
42560
- height: "24",
42561
- viewBox: "0 0 24 24",
42562
- className: css(styles$2.icon)
42563
- }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
42564
- id: "a",
42565
- d: "M7.401 10.035c-1.424.748-2.599 1.905-3.544 " + "3.48a1 1 0 0 1-1.714-1.03C4.325 8.849 7.652 7 " + "12 7c4.348 0 7.675 1.848 9.857 5.486a1 1 0 0 " + "1-1.714 1.028c-.945-1.574-2.12-2.73-3.544-" + "3.48a5 5 0 1 1-9.198 0zM12 15a3 3 0 1 0 0-6 3 3 " + "0 0 0 0 6z"
42566
- })), /*#__PURE__*/React.createElement("g", {
42567
- fill: "none",
42568
- fillRule: "evenodd"
42569
- }, /*#__PURE__*/React.createElement("path", {
42570
- fill: "none",
42571
- d: "M0 0h24v24H0z"
42572
- }), /*#__PURE__*/React.createElement("mask", {
42573
- id: "b",
42574
- fill: "#fff"
42575
- }, /*#__PURE__*/React.createElement("use", {
42576
- href: "#a"
42577
- })), /*#__PURE__*/React.createElement("use", {
42578
- fill: "#fff",
42579
- fillRule: "nonzero",
42580
- href: "#a"
42581
- }), /*#__PURE__*/React.createElement("g", {
42582
- fill: "#fff",
42583
- mask: "url(#b)"
42584
- }, /*#__PURE__*/React.createElement("path", {
42585
- d: "M0 0h24v24H0z"
42586
- }))));
42587
-
42588
- // Displays a stylized eye with a line through it: I don't want to see lint
42589
- const HiddenIcon = () => /*#__PURE__*/React.createElement("svg", {
42590
- width: "24",
42591
- height: "24",
42592
- viewBox: "0 0 24 24",
42593
- className: css(styles$2.icon)
42594
- }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
42595
- id: "a",
42596
- d: "M8.794 7.38C9.791 7.127 10.86 7 12 7c4.348 0 " + "7.675 1.848 9.857 5.486a1 1 0 0 1-1.714 " + "1.028c-.945-1.574-2.12-2.73-3.544-3.48.258." + "604.401 1.268.401 1.966 0 1.02-.305 " + "1.967-.828 2.757l2.535 2.536a1 1 0 0 " + "1-1.414 1.414l-12-12a1 1 0 0 1 " + "1.414-1.414L8.794 7.38zm5.914 5.913a3 3 0 0 " + "0-4.001-4.001l4 4.001zM6.072 8.486l2.976 " + "2.976a3 3 0 0 0 3.49 3.49l1.579 1.58A5 5 0 " + "0 1 7.4 10.035c-1.424.747-2.599 1.904-3.544 " + "3.478a1 1 0 0 1-1.714-1.028c1.049-1.75 " + "2.363-3.085 3.929-4z"
42597
- })), /*#__PURE__*/React.createElement("g", {
42598
- fill: "none",
42599
- fillRule: "evenodd"
42600
- }, /*#__PURE__*/React.createElement("path", {
42601
- fill: "none",
42602
- d: "M0 0h24v24H0z"
42603
- }), /*#__PURE__*/React.createElement("mask", {
42604
- id: "b",
42605
- fill: "#fff"
42606
- }, /*#__PURE__*/React.createElement("use", {
42607
- href: "#a"
42608
- })), /*#__PURE__*/React.createElement("use", {
42609
- fill: "#fff",
42610
- fillRule: "nonzero",
42611
- href: "#a"
42612
- }), /*#__PURE__*/React.createElement("g", {
42613
- fill: "#fff",
42614
- mask: "url(#b)"
42615
- }, /*#__PURE__*/React.createElement("path", {
42616
- d: "M0 0h24v24H0z"
42617
- }))));
42618
- const HUD = ({
42619
- message,
42620
- enabled,
42621
- onClick,
42622
- fixedPosition: _fixedPosition = true
42623
- }) => {
42624
- let state;
42625
- let icon;
42626
- if (enabled) {
42627
- state = styles$2.enabled;
42628
- icon = /*#__PURE__*/React.createElement(VisibleIcon, null);
42629
- } else {
42630
- state = styles$2.disabled;
42631
- icon = /*#__PURE__*/React.createElement(HiddenIcon, null);
42632
- }
42633
- return /*#__PURE__*/React.createElement("button", {
42634
- className: css(styles$2.hud, _fixedPosition && styles$2.hudFixedPosition, state),
42635
- onClick: e => {
42636
- onClick();
42637
- }
42638
- }, icon, message);
42639
- };
42640
- const styles$2 = StyleSheet.create({
42641
- hud: {
42642
- boxSizing: "border-box",
42643
- height: 36,
42644
- padding: "9px 16px",
42645
- borderRadius: 18,
42646
- fontFamily: boldFontFamily,
42647
- fontSize: "15px",
42648
- lineHeight: "18px",
42649
- color: white$1,
42650
- userSelect: "none",
42651
- borderWidth: 0 // <button> gives us a border by default
42652
- },
42653
-
42654
- // We want to render the element in-place in our fixtures, so allow us to
42655
- // render in the bottom corner in production and in-place in testing.
42656
- hudFixedPosition: {
42657
- bottom: 20,
42658
- position: "fixed",
42659
- right: 20,
42660
- zIndex: 1
42661
- },
42662
- icon: {
42663
- width: 24,
42664
- height: 24,
42665
- marginRight: 8,
42666
- marginTop: -3,
42667
- verticalAlign: "middle"
42668
- },
42669
- enabled: {
42670
- backgroundColor: warningColor,
42671
- ":hover": {
42672
- backgroundColor: warningColorHover
42673
- },
42674
- ":active": {
42675
- backgroundColor: warningColorActive
42676
- }
42677
- },
42678
- disabled: {
42679
- backgroundColor: gray76,
42680
- ":hover": {
42681
- backgroundColor: "#a1a5a9" // in between those two grays
42682
- },
42683
-
42684
- ":active": {
42685
- backgroundColor: gray68
42686
- }
42687
- }
42688
- });
42689
-
42690
- /**
42691
- * MultiButtonGroup is an aesthetically pleasing group of buttons,
42692
- * which allows multiple buttons to be selected at the same time.
42693
- *
42694
- * NOTE: This component is almost identical to ./button-group.jsx except that
42695
- * this component allows multiple selection!
42696
- */
42697
- class MultiButtonGroup extends React.Component {
42698
- constructor(...args) {
42699
- super(...args);
42700
- this.toggleSelect = newValue => {
42701
- const values = (this.props.values || []).slice(0);
42702
- const allowEmpty = this.props.allowEmpty;
42703
- if (values.indexOf(newValue) >= 0 && (values.length > 1 || allowEmpty)) {
42704
- // If the value is already selected, unselect it
42705
- values.splice(values.indexOf(newValue), 1);
42706
- } else {
42707
- // Otherwise merge with other values and return
42708
- if (values.indexOf(newValue) < 0) {
42709
- values.push(newValue);
42710
- }
42711
- }
42712
- this.props.onChange(values);
42713
- };
42714
- }
42715
- focus() {
42716
- var _ReactDOM$findDOMNode;
42717
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element | Text'.
42718
- (_ReactDOM$findDOMNode = ReactDOM.findDOMNode(this)) == null || _ReactDOM$findDOMNode.focus();
42719
- return true;
42720
- }
42721
- render() {
42722
- const values = this.props.values || [];
42723
- const buttons = this.props.buttons.map((button, i) => {
42724
- const selected = values.indexOf(button.value) >= 0;
42725
- return /*#__PURE__*/React.createElement("button", {
42726
- title: button.title,
42727
- type: "button",
42728
- id: "" + i,
42729
- key: "" + i,
42730
- ref: "button" + i,
42731
- className: css(styles$1.buttonStyle, selected && styles$1.selectedStyle),
42732
- onClick: () => this.toggleSelect(button.value)
42733
- }, button.content || "" + button.value);
42734
- });
42735
- const outerStyle = {
42736
- display: "inline-block"
42737
- };
42738
- return /*#__PURE__*/React.createElement("div", {
42739
- style: outerStyle
42740
- }, buttons);
42741
- }
42742
- }
42743
- MultiButtonGroup.defaultProps = {
42744
- values: [],
42745
- allowEmpty: true
42746
- };
42747
- const styles$1 = StyleSheet.create({
42748
- buttonStyle: {
42749
- backgroundColor: "white",
42750
- border: "1px solid #ccc",
42751
- borderLeft: "0",
42752
- cursor: "pointer",
42753
- margin: "0",
42754
- padding: "5px 10px",
42755
- position: "relative",
42756
- // for hover
42757
-
42758
- ":first-child": {
42759
- borderLeft: "1px solid #ccc",
42760
- borderTopLeftRadius: "3px",
42761
- borderBottomLeftRadius: "3px"
42762
- },
42763
- ":last-child": {
42764
- borderRight: "1px solid #ccc",
42765
- borderTopRightRadius: "3px",
42766
- borderBottomRightRadius: "3px"
42767
- },
42768
- ":hover": {
42769
- backgroundColor: "#ccc"
42770
- },
42771
- ":focus": {
42772
- zIndex: 2
42773
- }
42774
- },
42775
- selectedStyle: {
42776
- backgroundColor: "#ddd"
42777
- }
42778
- });
42779
-
42780
- /* eslint-disable @babel/no-invalid-this */
42781
-
42782
- /* A checkbox that syncs its value to props using the
42783
- * renderer's onChange method, and gets the prop name
42784
- * dynamically from its props list
42785
- */
42786
- const PropCheckBox = createReactClass({
42787
- displayName: "PropCheckBox",
42788
- propTypes: {
42789
- labelAlignment: PropTypes.oneOf(["left", "right"])
42790
- },
42791
- DEFAULT_PROPS: {
42792
- label: null,
42793
- onChange: null,
42794
- labelAlignment: "left"
42795
- },
42796
- getDefaultProps: function () {
42797
- return this.DEFAULT_PROPS;
42798
- },
42799
- propName: function () {
42800
- const propName = _.find(_.keys(this.props), function (localPropName) {
42801
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
42802
- return !_.has(this.DEFAULT_PROPS, localPropName);
42803
- }, this);
42804
- if (!propName) {
42805
- throw new PerseusError("Attempted to create a PropCheckBox with no prop!", Errors.InvalidInput);
42806
- }
42807
- return propName;
42808
- },
42809
- _labelAlignLeft: function () {
42810
- return this.props.labelAlignment === "left";
42811
- },
42812
- render: function () {
42813
- const propName = this.propName();
42814
- return /*#__PURE__*/React.createElement("label", null, this._labelAlignLeft() && this.props.label, /*#__PURE__*/React.createElement("input", {
42815
- type: "checkbox",
42816
- checked: this.props[propName],
42817
- onChange: this.toggle
42818
- }), !this._labelAlignLeft() && this.props.label);
42819
- },
42820
- toggle: function () {
42821
- const propName = this.propName();
42822
- const changes = {};
42823
- changes[propName] = !this.props[propName];
42824
- this.props.onChange(changes);
42825
- }
42826
- });
42827
-
42828
- const truth = () => true;
42829
-
42830
- /* A minor abstraction on top of NumberInput for ranges
42831
- *
42832
- */
42833
- class RangeInput extends React.Component {
42834
- constructor(...args) {
42835
- super(...args);
42836
- this.onChange = (i, newVal) => {
42837
- const value = this.props.value;
42838
- if (i === 0) {
42839
- this.props.onChange([newVal, value[1]]);
42840
- } else {
42841
- this.props.onChange([value[0], newVal]);
42842
- }
42843
- };
42844
- }
42845
- render() {
42846
- const value = this.props.value;
42847
- const checkValidity = this.props.checkValidity || truth;
42848
- return /*#__PURE__*/React.createElement("div", {
42849
- className: "range-input"
42850
- }, /*#__PURE__*/React.createElement(NumberInput, _extends({}, this.props, {
42851
- value: value[0],
42852
- checkValidity: val => checkValidity([val, value[1]])
42853
- // eslint-disable-next-line react/jsx-no-bind
42854
- ,
42855
- onChange: this.onChange.bind(this, 0),
42856
- placeholder: this.props.placeholder[0]
42857
- })), /*#__PURE__*/React.createElement(NumberInput, _extends({}, this.props, {
42858
- value: value[1],
42859
- checkValidity: val => checkValidity([value[0], val])
42860
- // eslint-disable-next-line react/jsx-no-bind
42861
- ,
42862
- onChange: this.onChange.bind(this, 1),
42863
- placeholder: this.props.placeholder[1]
42864
- })));
42865
- }
42866
- }
42867
- RangeInput.propTypes = {
42868
- value: PropTypes.array.isRequired,
42869
- onChange: PropTypes.func.isRequired,
42870
- placeholder: PropTypes.array,
42871
- checkValidity: PropTypes.func
42872
- };
42873
- RangeInput.defaultProps = {
42874
- placeholder: [null, null]
42875
- };
42876
-
42877
- var components = /*#__PURE__*/Object.freeze({
42878
- __proto__: null,
42879
- ButtonGroup: ButtonGroup,
42880
- Graph: Graph,
42881
- Graphie: Graphie,
42882
- HUD: HUD,
42883
- Icon: Icon,
42884
- InfoTip: InfoTip,
42885
- InlineIcon: InlineIcon,
42886
- MathInput: MathInput,
42887
- MultiButtonGroup: MultiButtonGroup,
42888
- NumberInput: NumberInput,
42889
- PropCheckBox: PropCheckBox,
42890
- RangeInput: RangeInput,
42891
- SvgImage: SvgImage,
42892
- TextInput: TextInput,
42893
- TextListEditor: TextListEditor
42894
- });
42895
-
42896
43131
  const devices = {
42897
43132
  PHONE: "phone",
42898
43133
  TABLET: "tablet",