@khanacademy/perseus 20.8.0 → 20.8.1

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/index.js CHANGED
@@ -12,9 +12,8 @@ var classNames = require('classnames');
12
12
  var React = require('react');
13
13
  var _ = require('underscore');
14
14
  var Clickable = require('@khanacademy/wonder-blocks-clickable');
15
- var Color = require('@khanacademy/wonder-blocks-color');
16
15
  var wonderBlocksPopover = require('@khanacademy/wonder-blocks-popover');
17
- var Spacing = require('@khanacademy/wonder-blocks-spacing');
16
+ var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
18
17
  var aphrodite = require('aphrodite');
19
18
  var $ = require('jquery');
20
19
  var PropTypes = require('prop-types');
@@ -33,7 +32,6 @@ var caretDown = require('@phosphor-icons/core/assets/regular/caret-down.svg');
33
32
  var caretUp = require('@phosphor-icons/core/assets/regular/caret-up.svg');
34
33
  var debounce$1 = require('lodash.debounce');
35
34
  var mafs = require('mafs');
36
- var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
37
35
  var wonderBlocksIcon = require('@khanacademy/wonder-blocks-icon');
38
36
  var questionIcon = require('@phosphor-icons/core/regular/question.svg');
39
37
  var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
@@ -73,8 +71,6 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
73
71
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
74
72
  var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
75
73
  var Clickable__default = /*#__PURE__*/_interopDefaultLegacy(Clickable);
76
- var Color__default = /*#__PURE__*/_interopDefaultLegacy(Color);
77
- var Spacing__default = /*#__PURE__*/_interopDefaultLegacy(Spacing);
78
74
  var $__default = /*#__PURE__*/_interopDefaultLegacy($);
79
75
  var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
80
76
  var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
@@ -361,16 +357,16 @@ const MathInputIcon = _ref => {
361
357
  focused,
362
358
  active
363
359
  } = _ref;
364
- let color;
360
+ let fillColor;
365
361
  switch (true) {
366
362
  case focused || active:
367
- color = Color__default["default"].white;
363
+ fillColor = wonderBlocksTokens.color.white;
368
364
  break;
369
365
  case hovered:
370
- color = Color__default["default"].blue;
366
+ fillColor = wonderBlocksTokens.color.blue;
371
367
  break;
372
368
  default:
373
- color = Color__default["default"].offBlack;
369
+ fillColor = wonderBlocksTokens.color.offBlack;
374
370
  break;
375
371
  }
376
372
  const dynamicClass = active || focused ? styles$D.iconActive : styles$D.iconInactive;
@@ -380,7 +376,7 @@ const MathInputIcon = _ref => {
380
376
  xmlns: "http://www.w3.org/2000/svg",
381
377
  width: "16",
382
378
  height: "16",
383
- fill: color,
379
+ fill: fillColor,
384
380
  viewBox: "0 0 256 256"
385
381
  }, /*#__PURE__*/React__namespace.createElement("path", {
386
382
  d: "M112,72a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16h64A8,8,0,0,1,112,72Zm-8,104H80V152a8,8,0,0,0-16,0v24H40a8,8,0,0,0,0,16H64v24a8,8,0,0,0,16,0V192h24a8,8,0,0,0,0-16Zm48,0h64a8,8,0,0,0,0-16H152a8,8,0,0,0,0,16Zm64,16H152a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16Zm-61.66-90.34a8,8,0,0,0,11.32,0L184,83.31l18.34,18.35a8,8,0,0,0,11.32-11.32L195.31,72l18.35-18.34a8,8,0,0,0-11.32-11.32L184,60.69,165.66,42.34a8,8,0,0,0-11.32,11.32L172.69,72,154.34,90.34A8,8,0,0,0,154.34,101.66Z"
@@ -417,7 +413,7 @@ const mapButtonSets = buttonSets => {
417
413
  };
418
414
  const inputFocused = {
419
415
  borderWidth: 2,
420
- borderColor: Color__default["default"].blue,
416
+ borderColor: wonderBlocksTokens.color.blue,
421
417
  margin: -1
422
418
  };
423
419
  const styles$D = aphrodite.StyleSheet.create({
@@ -425,35 +421,35 @@ const styles$D = aphrodite.StyleSheet.create({
425
421
  display: "flex",
426
422
  justifyContent: "center",
427
423
  height: "100%",
428
- padding: Spacing__default["default"].xxxSmall_4,
424
+ padding: wonderBlocksTokens.spacing.xxxSmall_4,
429
425
  borderRadius: 1
430
426
  },
431
427
  iconInactive: {
432
- backgroundColor: Color__default["default"].offBlack8
428
+ backgroundColor: wonderBlocksTokens.color.offBlack8
433
429
  },
434
430
  iconActive: {
435
- backgroundColor: Color__default["default"].offBlack64
431
+ backgroundColor: wonderBlocksTokens.color.offBlack64
436
432
  },
437
433
  outerWrapper: {
438
434
  display: "inline-block",
439
435
  borderStyle: "solid",
440
436
  borderWidth: 1,
441
- borderColor: Color__default["default"].offBlack50,
437
+ borderColor: wonderBlocksTokens.color.offBlack50,
442
438
  borderRadius: 3,
443
- background: Color__default["default"].white,
439
+ background: wonderBlocksTokens.color.white,
444
440
  ":hover": inputFocused
445
441
  },
446
442
  wrapperFocused: inputFocused,
447
443
  wrapperError: {
448
- borderColor: Color__default["default"].red,
449
- background: Color.fade(Color__default["default"].red, 0.08),
444
+ borderColor: wonderBlocksTokens.color.red,
445
+ background: wonderBlocksTokens.color.fadedRed8,
450
446
  ":hover": {
451
- borderColor: Color__default["default"].red
447
+ borderColor: wonderBlocksTokens.color.red
452
448
  }
453
449
  },
454
450
  popoverContent: {
455
451
  padding: 0,
456
- paddingBottom: Spacing__default["default"].xxSmall_6,
452
+ paddingBottom: wonderBlocksTokens.spacing.xxSmall_6,
457
453
  maxWidth: "initial"
458
454
  }
459
455
  });
@@ -21863,13 +21859,13 @@ const styles$A = aphrodite.StyleSheet.create({
21863
21859
  width: 40
21864
21860
  },
21865
21861
  leftAlign: {
21866
- paddingLeft: Spacing__default["default"].xxxSmall_4,
21862
+ paddingLeft: wonderBlocksTokens.spacing.xxxSmall_4,
21867
21863
  paddingRight: 0
21868
21864
  },
21869
21865
  rightAlign: {
21870
21866
  textAlign: "right",
21871
21867
  paddingLeft: 0,
21872
- paddingRight: Spacing__default["default"].xxxSmall_4
21868
+ paddingRight: wonderBlocksTokens.spacing.xxxSmall_4
21873
21869
  },
21874
21870
  answerStateUnanswered: {
21875
21871
  backgroundColor: "#eee",
@@ -22966,17 +22962,17 @@ function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, cor
22966
22962
  let borderColor;
22967
22963
  let color;
22968
22964
  if (!showCorrectness && pressed) {
22969
- borderColor = Color__default["default"].blue;
22970
- color = Color__default["default"].blue;
22965
+ borderColor = wonderBlocksTokens.color.blue;
22966
+ color = wonderBlocksTokens.color.blue;
22971
22967
  backgroundColor = "transparent";
22972
22968
  } else if (checked) {
22973
- const bg = showCorrectness && correct ? Color__default["default"].green : Color__default["default"].blue;
22969
+ const bg = showCorrectness && correct ? wonderBlocksTokens.color.green : wonderBlocksTokens.color.blue;
22974
22970
  color = white$1;
22975
22971
  backgroundColor = bg;
22976
22972
  borderColor = bg;
22977
22973
  } else {
22978
- borderColor = Color__default["default"].offBlack64;
22979
- color = Color__default["default"].offBlack64;
22974
+ borderColor = wonderBlocksTokens.color.offBlack64;
22975
+ color = wonderBlocksTokens.color.offBlack64;
22980
22976
  }
22981
22977
 
22982
22978
  // define shape
@@ -23010,7 +23006,7 @@ const ChoiceIcon = function (props) {
23010
23006
  return /*#__PURE__*/React__namespace.createElement("div", {
23011
23007
  className: aphrodite.css(styles$y.iconWrapper)
23012
23008
  }, /*#__PURE__*/React__namespace.createElement(FocusRing, {
23013
- color: Color__default["default"].blue,
23009
+ color: wonderBlocksTokens.color.blue,
23014
23010
  visible: focused || hovered,
23015
23011
  multipleSelect: multipleSelect
23016
23012
  }, /*#__PURE__*/React__namespace.createElement("div", {
@@ -23075,9 +23071,9 @@ const styles$w = aphrodite.StyleSheet.create({
23075
23071
  color: gray68
23076
23072
  },
23077
23073
  circleIncorrectAnswered: {
23078
- backgroundColor: Color__default["default"].red,
23079
- borderColor: Color__default["default"].red,
23080
- color: Color__default["default"].white
23074
+ backgroundColor: wonderBlocksTokens.color.red,
23075
+ borderColor: wonderBlocksTokens.color.red,
23076
+ color: wonderBlocksTokens.color.white
23081
23077
  }
23082
23078
  });
23083
23079
 
@@ -23145,13 +23141,13 @@ const styles$v = aphrodite.StyleSheet.create({
23145
23141
  textTransform: "uppercase"
23146
23142
  },
23147
23143
  correct: {
23148
- color: Color__default["default"].green
23144
+ color: wonderBlocksTokens.color.green
23149
23145
  },
23150
23146
  incorrectAnswered: {
23151
- color: Color__default["default"].red
23147
+ color: wonderBlocksTokens.color.red
23152
23148
  },
23153
23149
  incorrect: {
23154
- color: Color__default["default"].offBlack64
23150
+ color: wonderBlocksTokens.color.offBlack64
23155
23151
  }
23156
23152
  });
23157
23153
 
@@ -23225,7 +23221,7 @@ const Choice = function (props) {
23225
23221
  // @ts-expect-error - TS2322 - Type '{ dispay: string; flexDirection: "column"; color: string; }' is not assignable to type 'Properties<string | number, string & {}>'.
23226
23222
  dispay: "flex",
23227
23223
  flexDirection: "column",
23228
- color: Color__default["default"].offBlack
23224
+ color: wonderBlocksTokens.color.offBlack
23229
23225
  },
23230
23226
  className: descriptionClassName
23231
23227
  }, /*#__PURE__*/React__namespace.createElement("div", {
@@ -23268,7 +23264,7 @@ const Choice = function (props) {
23268
23264
  disabled: disabled || apiOptions.readOnly,
23269
23265
  style: {
23270
23266
  flex: 1,
23271
- color: Color__default["default"].offBlack,
23267
+ color: wonderBlocksTokens.color.offBlack,
23272
23268
  userSelect: "text"
23273
23269
  },
23274
23270
  ref: forwardedRef,
@@ -23285,9 +23281,9 @@ const Choice = function (props) {
23285
23281
  flexDirection: "row",
23286
23282
  justifyContent: "center",
23287
23283
  alignContent: "center",
23288
- paddingTop: Spacing__default["default"].xSmall_8,
23289
- paddingBottom: Spacing__default["default"].xSmall_8,
23290
- paddingLeft: Spacing__default["default"].xSmall_8
23284
+ paddingTop: wonderBlocksTokens.spacing.xSmall_8,
23285
+ paddingBottom: wonderBlocksTokens.spacing.xSmall_8,
23286
+ paddingLeft: wonderBlocksTokens.spacing.xSmall_8
23291
23287
  }
23292
23288
  }, /*#__PURE__*/React__namespace.createElement(ChoiceIcon, {
23293
23289
  pos: pos,
@@ -23303,7 +23299,7 @@ const Choice = function (props) {
23303
23299
  previouslyAnswered: previouslyAnswered
23304
23300
  }), /*#__PURE__*/React__namespace.createElement("span", {
23305
23301
  style: {
23306
- paddingLeft: Spacing__default["default"].small_12,
23302
+ paddingLeft: wonderBlocksTokens.spacing.small_12,
23307
23303
  textAlign: "left",
23308
23304
  flex: 1,
23309
23305
  paddingTop: 4
@@ -23326,7 +23322,7 @@ const Choice = function (props) {
23326
23322
  content: i18n__namespace._("Cross out option"),
23327
23323
  closeButtonVisible: true,
23328
23324
  actions: /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, /*#__PURE__*/React__namespace.createElement(wonderBlocksLayout.Strut, {
23329
- size: Spacing__default["default"].medium_16
23325
+ size: wonderBlocksTokens.spacing.medium_16
23330
23326
  }), /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
23331
23327
  kind: "primary",
23332
23328
  "aria-label": i18n__namespace._("Cross out Choice %(letter)s", {
@@ -23374,7 +23370,7 @@ const Choice = function (props) {
23374
23370
  return /*#__PURE__*/React__namespace.createElement(Icon, {
23375
23371
  icon: ellipsisHorizontalIcon,
23376
23372
  size: 3,
23377
- color: Color__default["default"].offBlack64
23373
+ color: wonderBlocksTokens.color.offBlack64
23378
23374
  });
23379
23375
  });
23380
23376
  })), showRationale && /*#__PURE__*/React__namespace.createElement("div", {
@@ -23392,7 +23388,7 @@ const styles$u = aphrodite.StyleSheet.create({
23392
23388
  padding: intermediateCheckboxPadding,
23393
23389
  paddingTop: 0,
23394
23390
  marginLeft: 54,
23395
- color: Color__default["default"].offBlack64,
23391
+ color: wonderBlocksTokens.color.offBlack64,
23396
23392
  [mediaQueries.smOrSmaller]: {
23397
23393
  padding: intermediateCheckboxPaddingPhone,
23398
23394
  paddingTop: 0
@@ -24818,8 +24814,8 @@ class Definition extends React__namespace.Component {
24818
24814
  } = _ref2;
24819
24815
  return /*#__PURE__*/React__namespace.createElement("span", {
24820
24816
  style: {
24821
- color: Color__default["default"].blue,
24822
- borderBottom: hovered || focused || pressed ? `2px solid ${Color__default["default"].blue}` : "none"
24817
+ color: wonderBlocksTokens.color.blue,
24818
+ borderBottom: hovered || focused || pressed ? `2px solid ${wonderBlocksTokens.color.blue}` : "none"
24823
24819
  }
24824
24820
  }, this.props.togglePrompt);
24825
24821
  }));
@@ -24828,7 +24824,7 @@ class Definition extends React__namespace.Component {
24828
24824
  }
24829
24825
  const styles$q = {
24830
24826
  tooltipBody: {
24831
- color: Color__default["default"].offBlack,
24827
+ color: wonderBlocksTokens.color.offBlack,
24832
24828
  fontSize: 20,
24833
24829
  fontWeight: 500,
24834
24830
  lineHeight: "30px"
@@ -25126,7 +25122,7 @@ class GradedGroupAnswerBar extends React__namespace.Component {
25126
25122
  } = this.props;
25127
25123
  const answerBarStyle = {
25128
25124
  ...styles$o.answerBar,
25129
- backgroundColor: answerBarState === "CORRECT" ? Color__default["default"].offWhite : "white",
25125
+ backgroundColor: answerBarState === "CORRECT" ? wonderBlocksTokens.color.offWhite : "white",
25130
25126
  // Center the "Correct!" message only when there's no next question
25131
25127
  justifyContent: answerBarState === "CORRECT" && !onNextQuestion ? "center" : "space-between"
25132
25128
  };
@@ -25156,7 +25152,7 @@ class GradedGroupAnswerBar extends React__namespace.Component {
25156
25152
  }, /*#__PURE__*/React__namespace.createElement("span", {
25157
25153
  style: {
25158
25154
  fontSize: 28,
25159
- color: Color__default["default"].green
25155
+ color: wonderBlocksTokens.color.green
25160
25156
  }
25161
25157
  }, /*#__PURE__*/React__namespace.createElement(InlineIcon, _extends({}, iconStar, {
25162
25158
  style: {
@@ -25186,7 +25182,7 @@ const styles$o = {
25186
25182
  marginTop: phoneMargin,
25187
25183
  paddingLeft: phoneMargin,
25188
25184
  paddingRight: 10,
25189
- borderTop: `1px solid ${Color__default["default"].offBlack50}`
25185
+ borderTop: `1px solid ${wonderBlocksTokens.color.offBlack50}`
25190
25186
  },
25191
25187
  tryAgainIcon: {
25192
25188
  fontSize: 28,
@@ -25494,7 +25490,7 @@ const styles$n = aphrodite.StyleSheet.create({
25494
25490
  padding: 0,
25495
25491
  border: "none",
25496
25492
  marginTop: 20,
25497
- color: Color__default["default"].blue,
25493
+ color: wonderBlocksTokens.color.blue,
25498
25494
  cursor: "pointer",
25499
25495
  display: "block",
25500
25496
  clear: "both"
@@ -25502,7 +25498,7 @@ const styles$n = aphrodite.StyleSheet.create({
25502
25498
  explanationTitle: {
25503
25499
  backgroundColor: "unset",
25504
25500
  marginTop: 20,
25505
- color: Color__default["default"].blue,
25501
+ color: wonderBlocksTokens.color.blue,
25506
25502
  marginBottom: 10,
25507
25503
  cursor: "pointer",
25508
25504
  fontSize: 14,
@@ -25718,7 +25714,7 @@ const styles$m = aphrodite.StyleSheet.create({
25718
25714
  },
25719
25715
  title: {
25720
25716
  fontSize: 12,
25721
- color: Color__default["default"].offBlack64,
25717
+ color: wonderBlocksTokens.color.offBlack64,
25722
25718
  textTransform: "uppercase",
25723
25719
  marginBottom: 11,
25724
25720
  letterSpacing: 0.8
@@ -25752,7 +25748,7 @@ const styles$m = aphrodite.StyleSheet.create({
25752
25748
  height: 10,
25753
25749
  borderRadius: "100%",
25754
25750
  borderWidth: 2,
25755
- borderColor: Color__default["default"].blue,
25751
+ borderColor: wonderBlocksTokens.color.blue,
25756
25752
  borderStyle: "solid"
25757
25753
  },
25758
25754
  indicatorDotFocused: {
@@ -25760,7 +25756,7 @@ const styles$m = aphrodite.StyleSheet.create({
25760
25756
  borderStyle: "double"
25761
25757
  },
25762
25758
  indicatorDotActive: {
25763
- backgroundColor: Color__default["default"].blue,
25759
+ backgroundColor: wonderBlocksTokens.color.blue,
25764
25760
  width: "100%",
25765
25761
  height: "100%"
25766
25762
  },
@@ -28370,6 +28366,57 @@ const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.
28370
28366
  return (coord + ranges[i][1]) / extent;
28371
28367
  }));
28372
28368
 
28369
+ const useTransform = function () {
28370
+ const {
28371
+ viewTransform,
28372
+ userTransform
28373
+ } = mafs.useTransformContext();
28374
+ const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
28375
+ for (var _len = arguments.length, points = new Array(_len), _key = 0; _key < _len; _key++) {
28376
+ points[_key] = arguments[_key];
28377
+ }
28378
+ const transformedPoints = points.map(point => mafs.vec.transform(point, transformToPx));
28379
+ return transformedPoints;
28380
+ };
28381
+
28382
+ /**
28383
+ * Given two points, find the tips that extends through the points to the edge of the range.
28384
+ * @param collinearPoint - The point that the line passes through. Needed to establish slope.
28385
+ * @param extendFrom - The point that the line extends from to the edge of the graph.
28386
+ */
28387
+ const getRayIntersectionCoords = (collinearPoint, extendFrom, range) => {
28388
+ // edges of the graph
28389
+ const [[xMin, xMax], [yMin, yMax]] = range;
28390
+ const [aX, aY] = collinearPoint;
28391
+ const [bX, bY] = extendFrom;
28392
+ const yDiff = bY - aY;
28393
+ const xDiff = bX - aX;
28394
+ const slope = yDiff / xDiff;
28395
+ const yAtXMin = slope * (xMin - aX) + aY;
28396
+ const yAtXMax = slope * (xMax - aX) + aY;
28397
+ const xAtYMin = (yMin - aY) / slope + aX;
28398
+ const xAtYMax = (yMax - aY) / slope + aX;
28399
+
28400
+ // clock analogy to describe quadrants
28401
+ switch (true) {
28402
+ // 12 o'clock to 2:59
28403
+ case yDiff > 0 && xDiff >= 0:
28404
+ return xAtYMax > xMax ? [xMax, yAtXMax] : [xAtYMax, yMax];
28405
+ // 3 o'clock to 5:59
28406
+ case yDiff <= 0 && xDiff > 0:
28407
+ // xAtYMin evaluates to -Infinity here, so we use absolute value
28408
+ return Math.abs(xAtYMin) > xMax ? [xMax, yAtXMax] : [xAtYMin, yMin];
28409
+ // 9 o'clock to 11:59
28410
+ case yDiff >= 0 && xDiff < 0:
28411
+ return xAtYMax < xMin ? [xMin, yAtXMin] : [xAtYMax, yMax];
28412
+ // 6 o'clock to 8:59
28413
+ case yDiff < 0 && xDiff <= 0:
28414
+ return xAtYMin < xMin ? [xMin, yAtXMin] : [xAtYMin, yMin];
28415
+ default:
28416
+ return [xMax, yAtXMax];
28417
+ }
28418
+ };
28419
+
28373
28420
  const defaultStroke = "var(--movable-line-stroke-color)";
28374
28421
  const MovableLine = props => {
28375
28422
  const {
@@ -28380,18 +28427,12 @@ const MovableLine = props => {
28380
28427
  stroke = defaultStroke
28381
28428
  } = props;
28382
28429
  const midpoint = mafs.vec.midpoint(start, end);
28383
- const {
28384
- viewTransform,
28385
- userTransform
28386
- } = mafs.useTransformContext();
28387
- const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
28388
- const startPtPx = mafs.vec.transform(start, transformToPx);
28389
- const endPtPx = mafs.vec.transform(end, transformToPx);
28430
+ const [startPtPx, endPtPx] = useTransform(start, end);
28390
28431
  let startExtend = undefined;
28391
28432
  let endExtend = undefined;
28392
28433
  if (extend) {
28393
- startExtend = extend.start ? getExtensionCoords(end, start, extend.range) : undefined;
28394
- endExtend = extend.end ? getExtensionCoords(start, end, extend.range) : undefined;
28434
+ startExtend = extend.start ? getRayIntersectionCoords(end, start, extend.range) : undefined;
28435
+ endExtend = extend.end ? getRayIntersectionCoords(start, end, extend.range) : undefined;
28395
28436
  }
28396
28437
  const line = React.useRef(null);
28397
28438
  const {
@@ -28453,50 +28494,12 @@ function SVGLine(props) {
28453
28494
  });
28454
28495
  }
28455
28496
 
28456
- /**
28457
- * Given two points, find the tips that extends through the points to the edge of the range.
28458
- * @param collinearPoint - The point that the line passes through. Needed to establish slope.
28459
- * @param extendFrom - The point that the line extends from to the edge of the graph.
28460
- */
28461
- const getExtensionCoords = (collinearPoint, extendFrom, range) => {
28462
- // edges of the graph
28463
- const [[xMin, xMax], [yMin, yMax]] = range;
28464
- const [aX, aY] = collinearPoint;
28465
- const [bX, bY] = extendFrom;
28466
- const yDiff = bY - aY;
28467
- const xDiff = bX - aX;
28468
- const slope = yDiff / xDiff;
28469
- const yAtXMin = slope * (xMin - aX) + aY;
28470
- const yAtXMax = slope * (xMax - aX) + aY;
28471
- const xAtYMin = (yMin - aY) / slope + aX;
28472
- const xAtYMax = (yMax - aY) / slope + aX;
28473
-
28474
- // clock analogy to describe quadrants
28475
- switch (true) {
28476
- // 12 o'clock to 2:59
28477
- case yDiff > 0 && xDiff >= 0:
28478
- return xAtYMax > xMax ? [xMax, yAtXMax] : [xAtYMax, yMax];
28479
- // 3 o'clock to 5:59
28480
- case yDiff <= 0 && xDiff > 0:
28481
- // xAtYMin evaluates to -Infinity here, so we use absolute value
28482
- return Math.abs(xAtYMin) > xMax ? [xMax, yAtXMax] : [xAtYMin, yMin];
28483
- // 9 o'clock to 11:59
28484
- case yDiff >= 0 && xDiff < 0:
28485
- return xAtYMax < xMin ? [xMin, yAtXMin] : [xAtYMax, yMax];
28486
- // 6 o'clock to 8:59
28487
- case yDiff < 0 && xDiff <= 0:
28488
- return xAtYMin < xMin ? [xMin, yAtXMin] : [xAtYMin, yMin];
28489
- default:
28490
- return [xMax, yAtXMax];
28491
- }
28492
- };
28493
-
28494
28497
  const StyledMovablePoint = props => {
28495
28498
  const hitboxRef = React.useRef(null);
28496
28499
  const {
28497
28500
  point,
28498
28501
  onMove,
28499
- color = Color__default["default"].blue
28502
+ color = wonderBlocksTokens.color.blue
28500
28503
  } = props;
28501
28504
  const {
28502
28505
  dragging
@@ -28506,12 +28509,7 @@ const StyledMovablePoint = props => {
28506
28509
  onMove,
28507
28510
  constrain: p => p
28508
28511
  });
28509
- const {
28510
- viewTransform,
28511
- userTransform
28512
- } = mafs.useTransformContext();
28513
- const transformToPx = mafs.vec.matrixMult(viewTransform, userTransform);
28514
- const [x, y] = mafs.vec.transform(point, transformToPx);
28512
+ const [[x, y]] = useTransform(point);
28515
28513
  return /*#__PURE__*/React__namespace.createElement("g", {
28516
28514
  ref: hitboxRef,
28517
28515
  className: "movable-point",
@@ -28678,6 +28676,161 @@ const RayGraph = props => {
28678
28676
  }));
28679
28677
  };
28680
28678
 
28679
+ const Angle = _ref => {
28680
+ let {
28681
+ centerPoint,
28682
+ endPoints,
28683
+ range,
28684
+ polygonPoints,
28685
+ showAngles
28686
+ } = _ref;
28687
+ const [centerX, centerY] = centerPoint;
28688
+ const areClockwise = clockwise$1([centerPoint, ...endPoints]);
28689
+ const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
28690
+ const radius = 0.3;
28691
+ const a = mafs.vec.dist(centerPoint, endPoints[0]);
28692
+ const b = mafs.vec.dist(centerPoint, endPoints[1]);
28693
+ const c = mafs.vec.dist(endPoints[0], endPoints[1]);
28694
+
28695
+ // Law of cosines
28696
+ const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
28697
+ const y1 = centerY + (startY - centerY) / a * radius;
28698
+ const x2 = centerX + (endX - centerX) / b * radius;
28699
+ const x1 = centerX + (startX - centerX) / a * radius;
28700
+ const y2 = centerY + (endY - centerY) / b * radius;
28701
+
28702
+ // Fourth point that would create a rhomboid
28703
+ // Used to calculate the inside of the angle and to create square
28704
+ const [x3, y3] = mafs.vec.add(centerPoint, mafs.vec.add(mafs.vec.sub([x1, y1], centerPoint), mafs.vec.sub([x2, y2], centerPoint)));
28705
+ if (!showAngles) {
28706
+ return isRightAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleArc, {
28707
+ start: [x1, y1],
28708
+ vertex: [x2, y2],
28709
+ end: [x3, y3]
28710
+ }) : null;
28711
+ }
28712
+
28713
+ // Midpoint betwen ends of arc
28714
+ const isInside = shouldDrawArcInside([x3, y3], centerPoint, range, polygonPoints);
28715
+ const largeArcFlag = isInside ? 1 : 0;
28716
+ const sweepFlag = isInside ? 1 : 0;
28717
+ const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
28718
+ let angleInDegrees = angle * (180 / Math.PI);
28719
+ // If we have triggered "largArcFlag", the angle should be greater than 180
28720
+ if (isInside) {
28721
+ angleInDegrees = 360 - angleInDegrees;
28722
+ }
28723
+ const angleLabelNumber = parseFloat(angleInDegrees.toFixed(1));
28724
+ const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
28725
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
28726
+ id: "background",
28727
+ x: "-5%",
28728
+ width: "110%",
28729
+ y: "0%",
28730
+ height: "100%"
28731
+ }, /*#__PURE__*/React__namespace.createElement("feFlood", {
28732
+ floodColor: "#FFF",
28733
+ floodOpacity: "0.5"
28734
+ }), /*#__PURE__*/React__namespace.createElement("feComposite", {
28735
+ operator: "over",
28736
+ in: "SourceGraphic"
28737
+ }))), !isInside && isRightAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleArc, {
28738
+ start: [x1, y1],
28739
+ vertex: [x2, y2],
28740
+ end: [x3, y3]
28741
+ }) : /*#__PURE__*/React__namespace.createElement("g", {
28742
+ style: {
28743
+ transform: `var(--mafs-view-transform) var(--mafs-user-transform)`
28744
+ }
28745
+ }, /*#__PURE__*/React__namespace.createElement("path", {
28746
+ d: arc,
28747
+ strokeWidth: 0.02,
28748
+ fill: "none"
28749
+ })), /*#__PURE__*/React__namespace.createElement(mafs.Text, {
28750
+ x: x3,
28751
+ y: y3,
28752
+ size: 15,
28753
+ svgTextProps: {
28754
+ filter: "url(#background)",
28755
+ fontWeight: "bold"
28756
+ }
28757
+ // Shift position if text is too close to movable point
28758
+ ,
28759
+ attach: y3 - centerY > 0 ? "s" : "n",
28760
+ attachDistance: Math.abs(y3 - centerY) < 0.2 || mafs.vec.dist([x3, y3], centerPoint) < 0.3 ? 20 : 10
28761
+ }, angleLabel, "\xB0"));
28762
+ };
28763
+ const RightAngleArc = _ref2 => {
28764
+ let {
28765
+ start: [x1, y1],
28766
+ vertex: [x2, y2],
28767
+ end: [x3, y3]
28768
+ } = _ref2;
28769
+ return /*#__PURE__*/React__namespace.createElement("g", {
28770
+ style: {
28771
+ transform: `var(--mafs-view-transform) var(--mafs-user-transform)`
28772
+ }
28773
+ }, /*#__PURE__*/React__namespace.createElement("path", {
28774
+ d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
28775
+ strokeWidth: 0.02,
28776
+ fill: "none"
28777
+ }));
28778
+ };
28779
+ const isRightAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
28780
+ const shouldDrawArcInside = (midPoint, vertex, range, polygonPoints) => {
28781
+ const rangeIntersectionPoint = getRayIntersectionCoords(vertex, midPoint, range);
28782
+ const lines = getLines(polygonPoints);
28783
+ let lineIntersections = 0;
28784
+ lines.forEach(line => linesIntersect([vertex, rangeIntersectionPoint], line) && lineIntersections++);
28785
+
28786
+ // The intersection check will sometimes return false if it intersects with
28787
+ // another vertex, so in the case we get 0 intersections, we check for points.
28788
+ if (lineIntersections === 0) {
28789
+ // find point in array
28790
+ const midpointIndex = polygonPoints.findIndex(_ref3 => {
28791
+ let [x, y] = _ref3;
28792
+ return x === midPoint[0] && y === midPoint[1];
28793
+ });
28794
+ // get array sans point and adjacent points
28795
+ const [prev, next] = [(midpointIndex - 1 + polygonPoints.length) % polygonPoints.length, (midpointIndex + 1) % polygonPoints.length];
28796
+ const [[lineAx, lineAy], [lineBx, lineBy]] = [midPoint, rangeIntersectionPoint];
28797
+ for (let i = 0; i < polygonPoints.length; i++) {
28798
+ if ([prev, midpointIndex, next].includes(i)) {
28799
+ continue;
28800
+ }
28801
+ const [x, y] = polygonPoints[i];
28802
+ const intersectsPoint = (y - lineAy) * (lineBx - lineAx) === (lineBy - lineAy) * (x - lineAx);
28803
+ if (intersectsPoint) {
28804
+ return false;
28805
+ }
28806
+ }
28807
+ return true;
28808
+ }
28809
+
28810
+ // If the number of intersections is even, the angle is inside the polygon
28811
+ return isEven(lineIntersections);
28812
+ };
28813
+ const getLines = points => points.map((point, i) => {
28814
+ const next = points[(i + 1) % points.length];
28815
+ return [point, next];
28816
+ });
28817
+
28818
+ // https://stackoverflow.com/a/24392281/7347484
28819
+ // The "intersects" function in geometry doesn't seem to work for this use case
28820
+ const linesIntersect = (_ref4, _ref5) => {
28821
+ let [[a, b], [c, d]] = _ref4;
28822
+ let [[p, q], [r, s]] = _ref5;
28823
+ const determinant = (c - a) * (s - q) - (r - p) * (d - b);
28824
+ if (determinant === 0) {
28825
+ return false;
28826
+ } else {
28827
+ const lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / determinant;
28828
+ const gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / determinant;
28829
+ return 0 < lambda && lambda < 1 && 0 < gamma && gamma < 1;
28830
+ }
28831
+ };
28832
+ const isEven = n => n % 2 === 0;
28833
+
28681
28834
  const PolygonGraph = props => {
28682
28835
  const [focused, setFocused] = React__namespace.useState(false);
28683
28836
  const [hovered, setHovered] = React__namespace.useState(false);
@@ -28686,7 +28839,9 @@ const PolygonGraph = props => {
28686
28839
  } = props;
28687
28840
  const {
28688
28841
  coords,
28689
- type
28842
+ type,
28843
+ showAngles,
28844
+ range
28690
28845
  } = props.graphState;
28691
28846
  const points = coords ?? [[0, 0]];
28692
28847
  const pointsSum = points.reduce((acc, point) => mafs.vec.add(acc, point), [0, 0]);
@@ -28710,6 +28865,21 @@ const PolygonGraph = props => {
28710
28865
  svgPolygonProps: {
28711
28866
  strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)"
28712
28867
  }
28868
+ }), points.map((point, i) => {
28869
+ const pt1 = points.at(i - 1);
28870
+ const pt2 = points[(i + 1) % points.length];
28871
+ if (!pt1 || !pt2) {
28872
+ return null;
28873
+ }
28874
+ return /*#__PURE__*/React__namespace.createElement(Angle, {
28875
+ key: "angle-" + i,
28876
+ centerPoint: point,
28877
+ endPoints: [pt1, pt2],
28878
+ active: active,
28879
+ range: range,
28880
+ polygonPoints: points,
28881
+ showAngles: !!showAngles
28882
+ });
28713
28883
  }), /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
28714
28884
  points: [...points],
28715
28885
  color: "transparent",
@@ -28727,7 +28897,7 @@ const PolygonGraph = props => {
28727
28897
  className: "movable-polygon"
28728
28898
  }
28729
28899
  }), points.map((point, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
28730
- key: i,
28900
+ key: "point-" + i,
28731
28901
  point: point,
28732
28902
  onMove: destination => dispatch(moveControlPoint(i, destination)),
28733
28903
  "data-testid": type + i
@@ -29409,7 +29579,11 @@ var components = /*#__PURE__*/Object.freeze({
29409
29579
  * If a graphie URL is provided in `backgroundImage`, will return the rendered graphie background.
29410
29580
  * Otherwise, returns `null`.
29411
29581
  */
29412
- const getLegacyGrid = (box, backgroundImage) => {
29582
+ const LegacyGrid = _ref => {
29583
+ let {
29584
+ box,
29585
+ backgroundImage
29586
+ } = _ref;
29413
29587
  const {
29414
29588
  url,
29415
29589
  width,
@@ -29423,10 +29597,10 @@ const getLegacyGrid = (box, backgroundImage) => {
29423
29597
  bottom: 0,
29424
29598
  left: 0
29425
29599
  }
29426
- }, /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null, _ref => {
29600
+ }, /*#__PURE__*/React__namespace.createElement(context$1.Consumer, null, _ref2 => {
29427
29601
  let {
29428
29602
  setAssetStatus
29429
- } = _ref;
29603
+ } = _ref2;
29430
29604
  return /*#__PURE__*/React__namespace.createElement(SvgImage, {
29431
29605
  src: url,
29432
29606
  width: width,
@@ -29698,10 +29872,10 @@ function initializeGraphState(params) {
29698
29872
  };
29699
29873
  case "polygon":
29700
29874
  return {
29701
- type: "polygon",
29702
29875
  hasBeenInteractedWith: false,
29703
29876
  range,
29704
29877
  snapStep,
29878
+ ...graph,
29705
29879
  coords: getPolygonCoords({
29706
29880
  graph,
29707
29881
  range,
@@ -29771,9 +29945,10 @@ const getDefaultSegments = _ref => {
29771
29945
  return [5];
29772
29946
  }
29773
29947
  };
29948
+ const defaultRange = [[-10, 10], [-10, 10]];
29774
29949
  return ys(graph.numSegments).map(y => {
29775
29950
  let endpoints = [[-5, y], [5, y]];
29776
- endpoints = normalizeCoords(endpoints, range);
29951
+ endpoints = normalizeCoords(endpoints, defaultRange);
29777
29952
  endpoints = normalizePoints(range, step, endpoints);
29778
29953
  return endpoints;
29779
29954
  });
@@ -29856,7 +30031,6 @@ const renderGraph = props => {
29856
30031
  };
29857
30032
  const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
29858
30033
  const [width, height] = props.box;
29859
- const legacyGrid = getLegacyGrid([width, height], props.backgroundImage);
29860
30034
  const [state, dispatch] = React__namespace.useReducer(interactiveGraphReducer, props, initializeGraphState);
29861
30035
  React__namespace.useImperativeHandle(ref, () => ({
29862
30036
  getUserInput: () => getGradableGraph(state, props.graph)
@@ -29867,13 +30041,17 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
29867
30041
  height,
29868
30042
  position: "relative"
29869
30043
  }
29870
- }, legacyGrid, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
30044
+ }, /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
30045
+ box: props.box,
30046
+ backgroundImage: props.backgroundImage
30047
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
29871
30048
  style: {
29872
30049
  position: "absolute",
29873
30050
  bottom: 0,
29874
30051
  left: 0
29875
30052
  }
29876
30053
  }, /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
30054
+ preserveAspectRatio: false,
29877
30055
  viewBox: {
29878
30056
  x: props.range[0],
29879
30057
  y: props.range[1],
@@ -29883,7 +30061,7 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
29883
30061
  zoom: false,
29884
30062
  width: width,
29885
30063
  height: height
29886
- }, !legacyGrid && /*#__PURE__*/React__namespace.createElement(Grid, props), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
30064
+ }, /*#__PURE__*/React__namespace.createElement(Grid, props), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
29887
30065
  lockedFigures: props.lockedFigures
29888
30066
  }), renderGraph({
29889
30067
  state,
@@ -32777,7 +32955,7 @@ const styles$g = aphrodite.StyleSheet.create({
32777
32955
  });
32778
32956
 
32779
32957
  const BringToFront = {
32780
- boxShadow: `0 8px 8px ${Color__default["default"].offBlack64}`,
32958
+ boxShadow: `0 8px 8px ${wonderBlocksTokens.color.offBlack64}`,
32781
32959
  zIndex: 1000
32782
32960
  };
32783
32961
  const AnswerPill = props => {
@@ -32833,7 +33011,7 @@ const styles$f = aphrodite.StyleSheet.create({
32833
33011
  backgroundColor: "#00880b"
32834
33012
  },
32835
33013
  incorrect: {
32836
- backgroundColor: Color__default["default"].offBlack64
33014
+ backgroundColor: wonderBlocksTokens.color.offBlack64
32837
33015
  },
32838
33016
  pill: {
32839
33017
  // Reset the Pill's default height in order to account
@@ -32877,7 +33055,7 @@ class Marker extends React__namespace.Component {
32877
33055
  // default dot
32878
33056
  let args = {
32879
33057
  size: MARKER_SIZE,
32880
- color: Color__default["default"].white,
33058
+ color: wonderBlocksTokens.color.white,
32881
33059
  icon: iconNull
32882
33060
  };
32883
33061
  if (showCorrectness) {
@@ -32938,7 +33116,7 @@ class Marker extends React__namespace.Component {
32938
33116
  const styles$e = aphrodite.StyleSheet.create({
32939
33117
  marker: {
32940
33118
  position: "absolute",
32941
- backgroundColor: Color__default["default"].white,
33119
+ backgroundColor: wonderBlocksTokens.color.white,
32942
33120
  borderRadius: MARKER_SIZE,
32943
33121
  // Center marker position based on its maximum size.
32944
33122
  width: MARKER_SIZE,
@@ -32946,7 +33124,7 @@ const styles$e = aphrodite.StyleSheet.create({
32946
33124
  marginLeft: MARKER_SIZE / -2,
32947
33125
  marginTop: MARKER_SIZE / -2,
32948
33126
  // Add a shadow to the marker to make it stand out from the image.
32949
- boxShadow: `0 8px 8px ${Color__default["default"].offBlack8}`
33127
+ boxShadow: `0 8px 8px ${wonderBlocksTokens.color.offBlack8}`
32950
33128
  },
32951
33129
  // The base and unfilled marker style.
32952
33130
  markerIcon: {
@@ -32956,7 +33134,7 @@ const styles$e = aphrodite.StyleSheet.create({
32956
33134
  boxSizing: "border-box",
32957
33135
  width: MARKER_SIZE,
32958
33136
  height: MARKER_SIZE,
32959
- border: `2px solid ${Color__default["default"].offBlack64}`,
33137
+ border: `2px solid ${wonderBlocksTokens.color.offBlack64}`,
32960
33138
  borderRadius: MARKER_SIZE
32961
33139
  },
32962
33140
  // The animation that presents the marker to the learner
@@ -32964,11 +33142,11 @@ const styles$e = aphrodite.StyleSheet.create({
32964
33142
  animationName: {
32965
33143
  "0%": {
32966
33144
  transform: "scale(1)",
32967
- backgroundColor: Color__default["default"].blue
33145
+ backgroundColor: wonderBlocksTokens.color.blue
32968
33146
  },
32969
33147
  "100%": {
32970
33148
  transform: "scale(1.3)",
32971
- backgroundColor: Color__default["default"].blue
33149
+ backgroundColor: wonderBlocksTokens.color.blue
32972
33150
  }
32973
33151
  },
32974
33152
  animationDirection: "alternate",
@@ -32985,35 +33163,35 @@ const styles$e = aphrodite.StyleSheet.create({
32985
33163
  animationIterationCount: "2"
32986
33164
  },
32987
33165
  markerActive: {
32988
- outline: `2px solid ${Color__default["default"].blue}`,
33166
+ outline: `2px solid ${wonderBlocksTokens.color.blue}`,
32989
33167
  outlineOffset: 2
32990
33168
  },
32991
33169
  // The learner is making an initial selection
32992
33170
  markerSelected: {
32993
- boxShadow: `0 8px 8px ${Color__default["default"].offBlack8}`,
32994
- border: `solid 4px ${Color__default["default"].white}`,
32995
- backgroundColor: Color__default["default"].blue,
33171
+ boxShadow: `0 8px 8px ${wonderBlocksTokens.color.offBlack8}`,
33172
+ border: `solid 4px ${wonderBlocksTokens.color.white}`,
33173
+ backgroundColor: wonderBlocksTokens.color.blue,
32996
33174
  borderRadius: MARKER_SIZE,
32997
33175
  transform: "rotate(180deg)"
32998
33176
  },
32999
33177
  // The learner has made a selection
33000
33178
  markerFilled: {
33001
33179
  backgroundColor: "#ECF3FE",
33002
- border: `4px solid ${Color__default["default"].blue}`
33180
+ border: `4px solid ${wonderBlocksTokens.color.blue}`
33003
33181
  },
33004
33182
  markerGraded: {
33005
33183
  width: MARKER_SIZE,
33006
33184
  height: MARKER_SIZE,
33007
33185
  justifyContent: "center",
33008
33186
  alignItems: "center",
33009
- border: `2px solid ${Color__default["default"].white}`
33187
+ border: `2px solid ${wonderBlocksTokens.color.white}`
33010
33188
  },
33011
33189
  markerCorrect: {
33012
33190
  background: "#00880b" // WB green darkened by 18%
33013
33191
  },
33014
33192
 
33015
33193
  markerIncorrect: {
33016
- background: Color__default["default"].offBlack64
33194
+ background: wonderBlocksTokens.color.offBlack64
33017
33195
  }
33018
33196
  });
33019
33197
 
@@ -33444,6 +33622,7 @@ class LabelImage extends React__namespace.Component {
33444
33622
  const answerChoicesActive = index === activeMarkerIndex;
33445
33623
  const showAnswerChoice = marker.selected && !answerChoicesActive && !this.state.hideAnswers;
33446
33624
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
33625
+ key: index,
33447
33626
  style: {
33448
33627
  position: "absolute",
33449
33628
  left: `${marker.x}%`,
@@ -38048,7 +38227,7 @@ const styles$8 = aphrodite.StyleSheet.create({
38048
38227
  // and the "Add highlight" action fails.
38049
38228
  userSelect: "none",
38050
38229
  fontFamily: `"Lato", sans-serif`,
38051
- padding: `10px ${Spacing__default["default"].medium_16}px`,
38230
+ padding: `10px ${wonderBlocksTokens.spacing.medium_16}px`,
38052
38231
  cursor: "pointer"
38053
38232
  }
38054
38233
  });
@@ -41894,7 +42073,7 @@ const init = function (options) {
41894
42073
 
41895
42074
  // This file is processed by a Rollup plugin (replace) to inject the production
41896
42075
  const libName = "@khanacademy/perseus";
41897
- const libVersion = "20.8.0";
42076
+ const libVersion = "20.8.1";
41898
42077
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
41899
42078
 
41900
42079
  const apiVersion = {
@@ -44777,15 +44956,14 @@ function getWidgetRegex() {
44777
44956
  }
44778
44957
 
44779
44958
  /**
44780
- * Add a widget placeholder using the provided widget type and instance number.
44781
- * ex. addWidget(WidgetType.Radio, 1) => "[[☃ radio 1]]"
44959
+ * Add a widget placeholder using the widget ID.
44960
+ * ex. addWidget("radio 1") => "[[☃ radio 1]]"
44782
44961
  *
44783
- * @param {WidgetType} widgetType
44784
- * @param {number} instance
44962
+ * @param {string} id
44785
44963
  * @returns {string}
44786
44964
  */
44787
- function addWidget(widgetType, instance) {
44788
- return `[[☃ ${widgetType} ${String(instance)}]]`;
44965
+ function addWidget(id) {
44966
+ return `[[☃ ${id}]]`;
44789
44967
  }
44790
44968
 
44791
44969
  /**