@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/es/index.js CHANGED
@@ -2,13 +2,13 @@ import * as KAS from '@khanacademy/kas';
2
2
  import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
3
3
  import * as PerseusLinter from '@khanacademy/perseus-linter';
4
4
  import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
5
- import { View, RenderStateRoot, UniqueIDProvider, useOnMountEffect, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
5
+ import { View, RenderStateRoot, Id, useOnMountEffect, useLatestRef, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
6
6
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
7
7
  import { HeadingMedium, LabelSmall, LabelLarge, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
8
8
  import { StyleSheet, css } from 'aphrodite';
9
9
  import classNames$1 from 'classnames';
10
10
  import * as React from 'react';
11
- import React__default, { useContext, useState, useEffect, useRef, createContext, forwardRef, useLayoutEffect, useImperativeHandle } from 'react';
11
+ import React__default, { useContext, useState, useEffect, useRef, createContext, forwardRef, useLayoutEffect, useImperativeHandle, useId } from 'react';
12
12
  import * as ReactDOM from 'react-dom';
13
13
  import ReactDOM__default from 'react-dom';
14
14
  import _, { debounce as debounce$1 } from 'underscore';
@@ -44,13 +44,21 @@ import cornersOutIcon from '@phosphor-icons/core/regular/corners-out.svg';
44
44
  import Link from '@khanacademy/wonder-blocks-link';
45
45
 
46
46
  function _extends() {
47
- return _extends = Object.assign ? Object.assign.bind() : function (n) {
48
- for (var e = 1; e < arguments.length; e++) {
49
- var t = arguments[e];
50
- for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
47
+ _extends = Object.assign || function (target) {
48
+ for (var i = 1; i < arguments.length; i++) {
49
+ var source = arguments[i];
50
+
51
+ for (var key in source) {
52
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
53
+ target[key] = source[key];
54
+ }
55
+ }
51
56
  }
52
- return n;
53
- }, _extends.apply(null, arguments);
57
+
58
+ return target;
59
+ };
60
+
61
+ return _extends.apply(this, arguments);
54
62
  }
55
63
 
56
64
  /**
@@ -4445,14 +4453,17 @@ const InteractiveUtil = {
4445
4453
  }
4446
4454
  };
4447
4455
 
4448
- function _classPrivateFieldBase(e, t) {
4449
- if (!{}.hasOwnProperty.call(e, t)) throw new TypeError("attempted to use private field on non-instance");
4450
- return e;
4456
+ function _classPrivateFieldBase(receiver, privateKey) {
4457
+ if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
4458
+ throw new TypeError("attempted to use private field on non-instance");
4459
+ }
4460
+
4461
+ return receiver;
4451
4462
  }
4452
4463
 
4453
4464
  var id = 0;
4454
- function _classPrivateFieldKey(e) {
4455
- return "__private_" + id++ + "_" + e;
4465
+ function _classPrivateFieldKey(name) {
4466
+ return "__private_" + id++ + "_" + name;
4456
4467
  }
4457
4468
 
4458
4469
  /*!
@@ -8859,13 +8870,10 @@ const reactRootCache = new WeakMap();
8859
8870
  */
8860
8871
  function render(element, container) {
8861
8872
  /**
8862
- * `RenderStateRoot` is responsible for tracking whether it's
8863
- * the initial render or subsequent renders. It's used by
8864
- * `useUniqueId` and will be used by `UniqueIDProvider` and
8865
- * `WithSSRPlaceholder` in the future. We're placing it as
8866
- * high up in the render tree as possible to ensure that any
8867
- * components using that hook or those components will function
8868
- * correctly.
8873
+ * `RenderStateRoot` is responsible for tracking whether it's the initial
8874
+ * render or subsequent renders. It's used by `WithSSRPlaceholder`. We're
8875
+ * placing it as high up in the render tree as possible to ensure that any
8876
+ * components using that hook or those components will function correctly.
8869
8877
  */
8870
8878
  const children = /*#__PURE__*/React.createElement(RenderStateRoot, {
8871
8879
  throwIfNested: false
@@ -21081,14 +21089,19 @@ TextInput.defaultProps = {
21081
21089
  disabled: false
21082
21090
  };
21083
21091
 
21084
- function _objectWithoutPropertiesLoose(r, e) {
21085
- if (null == r) return {};
21086
- var t = {};
21087
- for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
21088
- if (e.includes(n)) continue;
21089
- t[n] = r[n];
21092
+ function _objectWithoutPropertiesLoose(source, excluded) {
21093
+ if (source == null) return {};
21094
+ var target = {};
21095
+ var sourceKeys = Object.keys(source);
21096
+ var key, i;
21097
+
21098
+ for (i = 0; i < sourceKeys.length; i++) {
21099
+ key = sourceKeys[i];
21100
+ if (excluded.indexOf(key) >= 0) continue;
21101
+ target[key] = source[key];
21090
21102
  }
21091
- return t;
21103
+
21104
+ return target;
21092
21105
  }
21093
21106
 
21094
21107
  const _excluded$7 = ["position", "visibility", "left", "top"];
@@ -27369,10 +27382,7 @@ class Dropdown extends React.Component {
27369
27382
  value: String(i + 1),
27370
27383
  label: choice
27371
27384
  }))];
27372
- return /*#__PURE__*/React.createElement(UniqueIDProvider, {
27373
- scope: "dropdown-widget",
27374
- mockOnFirstRender: true
27375
- }, ids => /*#__PURE__*/React.createElement(View
27385
+ return /*#__PURE__*/React.createElement(Id, null, dropdownId => /*#__PURE__*/React.createElement(View
27376
27386
  // NOTE(jared): These are required to prevent weird behavior
27377
27387
  // When there's a dropdown in a zoomable table.
27378
27388
  , {
@@ -27384,9 +27394,9 @@ class Dropdown extends React.Component {
27384
27394
  }
27385
27395
  }, this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelLarge, {
27386
27396
  tag: "label",
27387
- htmlFor: ids.get("dropdown")
27397
+ htmlFor: dropdownId
27388
27398
  }, this.props.visibleLabel), /*#__PURE__*/React.createElement(SingleSelect, {
27389
- id: ids.get("dropdown"),
27399
+ id: dropdownId,
27390
27400
  placeholder: "",
27391
27401
  onChange: value => this._handleChange(parseInt(value)),
27392
27402
  selectedValue: String(this.props.selected),
@@ -27501,12 +27511,9 @@ class Explanation extends React.Component {
27501
27511
  "white-space": "normal"
27502
27512
  };
27503
27513
  const contentStyling = [styles$k.content, this.state.expanded ? styles$k.contentExpanded : styles$k.contentCollapsed, allowTransition && (this.state.expanded ? styles$k.transitionExpanded : styles$k.transitionCollapsed)];
27504
- return /*#__PURE__*/React.createElement(UniqueIDProvider, {
27505
- mockOnFirstRender: true,
27506
- scope: "explanation-widget"
27507
- }, ids => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
27514
+ return /*#__PURE__*/React.createElement(Id, null, contentId => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
27508
27515
  "aria-expanded": this.state.expanded,
27509
- "aria-controls": ids.get("content"),
27516
+ "aria-controls": contentId,
27510
27517
  endIcon: caretIcon,
27511
27518
  kind: "tertiary",
27512
27519
  labelStyle: labelStyle,
@@ -27514,7 +27521,7 @@ class Explanation extends React.Component {
27514
27521
  size: "small",
27515
27522
  style: buttonStyleOverrides
27516
27523
  }, promptText), /*#__PURE__*/React.createElement(View, {
27517
- id: ids.get("content"),
27524
+ id: contentId,
27518
27525
  style: contentStyling,
27519
27526
  "aria-hidden": !this.state.expanded,
27520
27527
  testId: "content-container"
@@ -32682,6 +32689,17 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
32682
32689
  function isBetween(x, low, high) {
32683
32690
  return x >= low && x <= high;
32684
32691
  }
32692
+ function getArrayWithoutDuplicates(array) {
32693
+ const returnArray = [];
32694
+ for (const coordPair of array) {
32695
+ if (
32696
+ // Check if the coordPair is not already in the returnArray
32697
+ !returnArray.some(([x, y]) => x === coordPair[0] && y === coordPair[1])) {
32698
+ returnArray.push(coordPair);
32699
+ }
32700
+ }
32701
+ return returnArray;
32702
+ }
32685
32703
 
32686
32704
  const LockedLine = props => {
32687
32705
  var _flags$mafs;
@@ -34092,6 +34110,12 @@ function getGradableGraph(state, initialGraph) {
34092
34110
  });
34093
34111
  }
34094
34112
  if (state.type === "polygon" && initialGraph.type === "polygon") {
34113
+ // Unless the polygon is closed it is not considered score-able.
34114
+ if (state.numSides === "unlimited" && !state.closedPolygon) {
34115
+ return _extends({}, initialGraph, {
34116
+ coords: null
34117
+ });
34118
+ }
34095
34119
  return _extends({}, initialGraph, {
34096
34120
  coords: state.coords
34097
34121
  });
@@ -34603,6 +34627,15 @@ const PolygonGraph = props => {
34603
34627
  (_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
34604
34628
  }
34605
34629
  }, [props.graphState.focusedPointIndex, pointsRef]);
34630
+
34631
+ // If the unlimited polygon is rendered with 3 or more coordinates
34632
+ // Close the polygon, but only on first render.
34633
+ React.useEffect(() => {
34634
+ if (numSides === "unlimited" && props.graphState.coords.length > 2) {
34635
+ dispatch(actions.polygon.closePolygon());
34636
+ }
34637
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34638
+ }, []);
34606
34639
  const statefulProps = _extends({}, props, {
34607
34640
  graphConfig,
34608
34641
  polygonRef,
@@ -34743,7 +34776,16 @@ const UnlimitedPolygonGraph = statefulProps => {
34743
34776
  } = graphConfig;
34744
34777
  const widthPx = graphDimensionsInPixels[0];
34745
34778
  const heightPx = graphDimensionsInPixels[1];
34746
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
34779
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Polyline, {
34780
+ points: [...points],
34781
+ color: "var(--movable-line-stroke-color)",
34782
+ svgPolylineProps: {
34783
+ strokeWidth: "var(--movable-line-stroke-weight)",
34784
+ style: {
34785
+ fill: "transparent"
34786
+ }
34787
+ }
34788
+ }), /*#__PURE__*/React.createElement("rect", {
34747
34789
  style: {
34748
34790
  fill: "rgba(0,0,0,0)",
34749
34791
  cursor: "crosshair"
@@ -34759,15 +34801,6 @@ const UnlimitedPolygonGraph = statefulProps => {
34759
34801
  const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
34760
34802
  dispatch(actions.polygon.addPoint(graphCoordinates[0]));
34761
34803
  }
34762
- }), /*#__PURE__*/React.createElement(Polyline, {
34763
- points: [...points],
34764
- color: "var(--movable-line-stroke-color)",
34765
- svgPolylineProps: {
34766
- strokeWidth: "var(--movable-line-stroke-weight)",
34767
- style: {
34768
- fill: "transparent"
34769
- }
34770
- }
34771
34804
  }), coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
34772
34805
  key: i,
34773
34806
  point: point,
@@ -34781,9 +34814,10 @@ const UnlimitedPolygonGraph = statefulProps => {
34781
34814
  },
34782
34815
  onClick: () => {
34783
34816
  // If the point being clicked is the first point and
34784
- // there's enough points to form a polygon (3 or more)
34785
- // Close the shape before setting focus.
34786
- if (i === 0 && coords.length >= 3) {
34817
+ // there's enough non-duplicated points to form
34818
+ // a polygon (3 or more), close the shape before
34819
+ // setting focus.
34820
+ if (i === 0 && getArrayWithoutDuplicates(coords).length >= 3) {
34787
34821
  dispatch(actions.polygon.closePolygon());
34788
34822
  }
34789
34823
  dispatch(actions.polygon.clickPoint(i));
@@ -35406,7 +35440,7 @@ const renderPolygonGraphControls = props => {
35406
35440
 
35407
35441
  // We want to disable the closePolygon button when
35408
35442
  // there are not enough coords to make a polygon
35409
- const disableCloseButton = coords.length < 3;
35443
+ const disableCloseButton = getArrayWithoutDuplicates(coords).length < 3;
35410
35444
 
35411
35445
  // If polygon is closed, show open button.
35412
35446
  // If polygon is open, show close button.
@@ -36106,7 +36140,13 @@ function doClickPoint(state, action) {
36106
36140
  }
36107
36141
  function doClosePolygon(state) {
36108
36142
  if (isUnlimitedGraphState(state) && state.type === "polygon") {
36143
+ // We want to remove any duplicate points when closing the polygon to
36144
+ // (1) prevent the polygon from sides with length zero, and
36145
+ // (2) make sure the question is can be marked correct if the polygon
36146
+ // LOOKS correct, even if two of the points are at the same coords.
36147
+ const noDupedPoints = getArrayWithoutDuplicates(state.coords);
36109
36148
  return _extends({}, state, {
36149
+ coords: noDupedPoints,
36110
36150
  closedPolygon: true
36111
36151
  });
36112
36152
  }
@@ -38888,9 +38928,8 @@ const AnswerChoices = props => {
38888
38928
  };
38889
38929
 
38890
38930
  const HideAnswersToggle = props => {
38891
- const idFactory = useUniqueIdWithMock();
38892
- const switchId = idFactory.get("hide-answers-toggle");
38893
- const labelId = idFactory.get("hide-answers-label");
38931
+ const switchId = useId();
38932
+ const labelId = useId();
38894
38933
  const {
38895
38934
  strings
38896
38935
  } = usePerseusI18n();
@@ -38933,7 +38972,7 @@ const AnswerPill = props => {
38933
38972
  focused,
38934
38973
  hovered
38935
38974
  } = props;
38936
- const idFactory = useUniqueIdWithMock();
38975
+ const pillId = useId();
38937
38976
  const {
38938
38977
  strings
38939
38978
  } = usePerseusI18n();
@@ -38960,7 +38999,7 @@ const AnswerPill = props => {
38960
38999
  }) => /*#__PURE__*/React.createElement(Pill, {
38961
39000
  size: "large",
38962
39001
  kind: "accent",
38963
- id: idFactory.get("perseus-label-image-widget-answer-pill"),
39002
+ id: pillId,
38964
39003
  onClick: correct ? undefined : onClick,
38965
39004
  ref: ref,
38966
39005
  style: [style, popperStyle, styles$b.pill, correct && styles$b.correct, incorrect && styles$b.incorrect, (focused || hovered) && BringToFront]
@@ -45276,7 +45315,7 @@ const init = function init() {
45276
45315
 
45277
45316
  // This file is processed by a Rollup plugin (replace) to inject the production
45278
45317
  const libName = "@khanacademy/perseus";
45279
- const libVersion = "48.2.0";
45318
+ const libVersion = "49.0.1";
45280
45319
  addLibraryVersionToPerseusDebug(libName, libVersion);
45281
45320
 
45282
45321
  const apiVersion = {