@khanacademy/perseus 48.2.0 → 49.0.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
@@ -8993,13 +8993,10 @@ const reactRootCache = new WeakMap();
8993
8993
  */
8994
8994
  function render(element, container) {
8995
8995
  /**
8996
- * `RenderStateRoot` is responsible for tracking whether it's
8997
- * the initial render or subsequent renders. It's used by
8998
- * `useUniqueId` and will be used by `UniqueIDProvider` and
8999
- * `WithSSRPlaceholder` in the future. We're placing it as
9000
- * high up in the render tree as possible to ensure that any
9001
- * components using that hook or those components will function
9002
- * correctly.
8996
+ * `RenderStateRoot` is responsible for tracking whether it's the initial
8997
+ * render or subsequent renders. It's used by `WithSSRPlaceholder`. We're
8998
+ * placing it as high up in the render tree as possible to ensure that any
8999
+ * components using that hook or those components will function correctly.
9003
9000
  */
9004
9001
  const children = /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.RenderStateRoot, {
9005
9002
  throwIfNested: false
@@ -27638,10 +27635,7 @@ class Dropdown extends React__namespace.Component {
27638
27635
  value: String(i + 1),
27639
27636
  label: choice
27640
27637
  }))];
27641
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.UniqueIDProvider, {
27642
- scope: "dropdown-widget",
27643
- mockOnFirstRender: true
27644
- }, ids => /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View
27638
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.Id, null, dropdownId => /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View
27645
27639
  // NOTE(jared): These are required to prevent weird behavior
27646
27640
  // When there's a dropdown in a zoomable table.
27647
27641
  , {
@@ -27653,9 +27647,9 @@ class Dropdown extends React__namespace.Component {
27653
27647
  }
27654
27648
  }, this.props.visibleLabel && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, {
27655
27649
  tag: "label",
27656
- htmlFor: ids.get("dropdown")
27650
+ htmlFor: dropdownId
27657
27651
  }, this.props.visibleLabel), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
27658
- id: ids.get("dropdown"),
27652
+ id: dropdownId,
27659
27653
  placeholder: "",
27660
27654
  onChange: value => this._handleChange(parseInt(value)),
27661
27655
  selectedValue: String(this.props.selected),
@@ -27775,12 +27769,9 @@ class Explanation extends React__namespace.Component {
27775
27769
  "white-space": "normal"
27776
27770
  };
27777
27771
  const contentStyling = [styles$k.content, this.state.expanded ? styles$k.contentExpanded : styles$k.contentCollapsed, allowTransition && (this.state.expanded ? styles$k.transitionExpanded : styles$k.transitionCollapsed)];
27778
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.UniqueIDProvider, {
27779
- mockOnFirstRender: true,
27780
- scope: "explanation-widget"
27781
- }, ids => /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
27772
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.Id, null, contentId => /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
27782
27773
  "aria-expanded": this.state.expanded,
27783
- "aria-controls": ids.get("content"),
27774
+ "aria-controls": contentId,
27784
27775
  endIcon: caretIcon,
27785
27776
  kind: "tertiary",
27786
27777
  labelStyle: labelStyle,
@@ -27788,7 +27779,7 @@ class Explanation extends React__namespace.Component {
27788
27779
  size: "small",
27789
27780
  style: buttonStyleOverrides
27790
27781
  }, promptText), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
27791
- id: ids.get("content"),
27782
+ id: contentId,
27792
27783
  style: contentStyling,
27793
27784
  "aria-hidden": !this.state.expanded,
27794
27785
  testId: "content-container"
@@ -32988,6 +32979,20 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
32988
32979
  function isBetween(x, low, high) {
32989
32980
  return x >= low && x <= high;
32990
32981
  }
32982
+ function getArrayWithoutDuplicates(array) {
32983
+ const returnArray = [];
32984
+ for (const coordPair of array) {
32985
+ if (
32986
+ // Check if the coordPair is not already in the returnArray
32987
+ !returnArray.some(_ref => {
32988
+ let [x, y] = _ref;
32989
+ return x === coordPair[0] && y === coordPair[1];
32990
+ })) {
32991
+ returnArray.push(coordPair);
32992
+ }
32993
+ }
32994
+ return returnArray;
32995
+ }
32991
32996
 
32992
32997
  const LockedLine = props => {
32993
32998
  const {
@@ -34414,6 +34419,13 @@ function getGradableGraph(state, initialGraph) {
34414
34419
  };
34415
34420
  }
34416
34421
  if (state.type === "polygon" && initialGraph.type === "polygon") {
34422
+ // Unless the polygon is closed it is not considered score-able.
34423
+ if (state.numSides === "unlimited" && !state.closedPolygon) {
34424
+ return {
34425
+ ...initialGraph,
34426
+ coords: null
34427
+ };
34428
+ }
34417
34429
  return {
34418
34430
  ...initialGraph,
34419
34431
  coords: state.coords
@@ -34934,6 +34946,15 @@ const PolygonGraph = props => {
34934
34946
  pointsRef.current[focusedIndex]?.focus();
34935
34947
  }
34936
34948
  }, [props.graphState.focusedPointIndex, pointsRef]);
34949
+
34950
+ // If the unlimited polygon is rendered with 3 or more coordinates
34951
+ // Close the polygon, but only on first render.
34952
+ React__namespace.useEffect(() => {
34953
+ if (numSides === "unlimited" && props.graphState.coords.length > 2) {
34954
+ dispatch(actions.polygon.closePolygon());
34955
+ }
34956
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34957
+ }, []);
34937
34958
  const statefulProps = {
34938
34959
  ...props,
34939
34960
  graphConfig,
@@ -35077,7 +35098,16 @@ const UnlimitedPolygonGraph = statefulProps => {
35077
35098
  } = graphConfig;
35078
35099
  const widthPx = graphDimensionsInPixels[0];
35079
35100
  const heightPx = graphDimensionsInPixels[1];
35080
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
35101
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
35102
+ points: [...points],
35103
+ color: "var(--movable-line-stroke-color)",
35104
+ svgPolylineProps: {
35105
+ strokeWidth: "var(--movable-line-stroke-weight)",
35106
+ style: {
35107
+ fill: "transparent"
35108
+ }
35109
+ }
35110
+ }), /*#__PURE__*/React__namespace.createElement("rect", {
35081
35111
  style: {
35082
35112
  fill: "rgba(0,0,0,0)",
35083
35113
  cursor: "crosshair"
@@ -35093,15 +35123,6 @@ const UnlimitedPolygonGraph = statefulProps => {
35093
35123
  const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
35094
35124
  dispatch(actions.polygon.addPoint(graphCoordinates[0]));
35095
35125
  }
35096
- }), /*#__PURE__*/React__namespace.createElement(mafs.Polyline, {
35097
- points: [...points],
35098
- color: "var(--movable-line-stroke-color)",
35099
- svgPolylineProps: {
35100
- strokeWidth: "var(--movable-line-stroke-weight)",
35101
- style: {
35102
- fill: "transparent"
35103
- }
35104
- }
35105
35126
  }), coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
35106
35127
  key: i,
35107
35128
  point: point,
@@ -35115,9 +35136,10 @@ const UnlimitedPolygonGraph = statefulProps => {
35115
35136
  },
35116
35137
  onClick: () => {
35117
35138
  // If the point being clicked is the first point and
35118
- // there's enough points to form a polygon (3 or more)
35119
- // Close the shape before setting focus.
35120
- if (i === 0 && coords.length >= 3) {
35139
+ // there's enough non-duplicated points to form
35140
+ // a polygon (3 or more), close the shape before
35141
+ // setting focus.
35142
+ if (i === 0 && getArrayWithoutDuplicates(coords).length >= 3) {
35121
35143
  dispatch(actions.polygon.closePolygon());
35122
35144
  }
35123
35145
  dispatch(actions.polygon.clickPoint(i));
@@ -35736,7 +35758,7 @@ const renderPolygonGraphControls = props => {
35736
35758
 
35737
35759
  // We want to disable the closePolygon button when
35738
35760
  // there are not enough coords to make a polygon
35739
- const disableCloseButton = coords.length < 3;
35761
+ const disableCloseButton = getArrayWithoutDuplicates(coords).length < 3;
35740
35762
 
35741
35763
  // If polygon is closed, show open button.
35742
35764
  // If polygon is open, show close button.
@@ -36461,8 +36483,14 @@ function doClickPoint(state, action) {
36461
36483
  }
36462
36484
  function doClosePolygon(state) {
36463
36485
  if (isUnlimitedGraphState(state) && state.type === "polygon") {
36486
+ // We want to remove any duplicate points when closing the polygon to
36487
+ // (1) prevent the polygon from sides with length zero, and
36488
+ // (2) make sure the question is can be marked correct if the polygon
36489
+ // LOOKS correct, even if two of the points are at the same coords.
36490
+ const noDupedPoints = getArrayWithoutDuplicates(state.coords);
36464
36491
  return {
36465
36492
  ...state,
36493
+ coords: noDupedPoints,
36466
36494
  closedPolygon: true
36467
36495
  };
36468
36496
  }
@@ -39251,9 +39279,8 @@ const AnswerChoices = props => {
39251
39279
  };
39252
39280
 
39253
39281
  const HideAnswersToggle = props => {
39254
- const idFactory = wonderBlocksCore.useUniqueIdWithMock();
39255
- const switchId = idFactory.get("hide-answers-toggle");
39256
- const labelId = idFactory.get("hide-answers-label");
39282
+ const switchId = React.useId();
39283
+ const labelId = React.useId();
39257
39284
  const {
39258
39285
  strings
39259
39286
  } = usePerseusI18n();
@@ -39296,7 +39323,7 @@ const AnswerPill = props => {
39296
39323
  focused,
39297
39324
  hovered
39298
39325
  } = props;
39299
- const idFactory = wonderBlocksCore.useUniqueIdWithMock();
39326
+ const pillId = React.useId();
39300
39327
  const {
39301
39328
  strings
39302
39329
  } = usePerseusI18n();
@@ -39325,7 +39352,7 @@ const AnswerPill = props => {
39325
39352
  return /*#__PURE__*/React__namespace.createElement(Pill__default["default"], {
39326
39353
  size: "large",
39327
39354
  kind: "accent",
39328
- id: idFactory.get("perseus-label-image-widget-answer-pill"),
39355
+ id: pillId,
39329
39356
  onClick: correct ? undefined : onClick,
39330
39357
  ref: ref,
39331
39358
  style: [style, popperStyle, styles$b.pill, correct && styles$b.correct, incorrect && styles$b.incorrect, (focused || hovered) && BringToFront]
@@ -45643,7 +45670,7 @@ const init = function () {
45643
45670
 
45644
45671
  // This file is processed by a Rollup plugin (replace) to inject the production
45645
45672
  const libName = "@khanacademy/perseus";
45646
- const libVersion = "48.2.0";
45673
+ const libVersion = "49.0.1";
45647
45674
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45648
45675
 
45649
45676
  const apiVersion = {