@khanacademy/perseus 20.6.0 → 20.8.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
@@ -31,7 +31,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
31
31
  import caretDown from '@phosphor-icons/core/assets/regular/caret-down.svg';
32
32
  import caretUp from '@phosphor-icons/core/assets/regular/caret-up.svg';
33
33
  import debounce$2 from 'lodash.debounce';
34
- import { Point as Point$2, vec, useTransformContext, useMovable, Vector, Coordinates, Mafs } from 'mafs';
34
+ import { Point as Point$2, vec, useTransformContext, useMovable, Vector, Polygon, Coordinates, Mafs } from 'mafs';
35
35
  import { color } from '@khanacademy/wonder-blocks-tokens';
36
36
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
37
37
  import questionIcon from '@phosphor-icons/core/regular/question.svg';
@@ -18834,7 +18834,7 @@ const isAccessible = widgetInfo => {
18834
18834
  }
18835
18835
  return !!accessible;
18836
18836
  };
18837
- const getAllWidgetTypes = () => {
18837
+ const getAllWidgetTypes$1 = () => {
18838
18838
  return _.keys(widgets);
18839
18839
  };
18840
18840
  const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
@@ -19103,7 +19103,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
19103
19103
  getVersionVector: getVersionVector,
19104
19104
  getPublicWidgets: getPublicWidgets,
19105
19105
  isAccessible: isAccessible,
19106
- getAllWidgetTypes: getAllWidgetTypes,
19106
+ getAllWidgetTypes: getAllWidgetTypes$1,
19107
19107
  upgradeWidgetInfoToLatestVersion: upgradeWidgetInfoToLatestVersion,
19108
19108
  getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
19109
19109
  traverseChildWidgets: traverseChildWidgets$3,
@@ -28166,22 +28166,55 @@ const GraphLockedLayer = props => {
28166
28166
  };
28167
28167
 
28168
28168
  const MOVE_CONTROL_POINT = "move-control-point";
28169
- function moveControlPoint(objectIndex, pointIndex, destination) {
28169
+ function moveControlPoint(pointIndex, destination, itemIndex) {
28170
28170
  return {
28171
28171
  type: MOVE_CONTROL_POINT,
28172
- objectIndex,
28172
+ itemIndex,
28173
28173
  pointIndex,
28174
28174
  destination
28175
28175
  };
28176
28176
  }
28177
+ const MOVE_ALL = "move-all";
28177
28178
  const MOVE_LINE = "move-line";
28178
- function moveSegment(lineIndex, delta) {
28179
+ /** This action assumes the state.coords holds an array of collinear tuples that define lines */
28180
+ function moveLine(itemIndex, delta) {
28179
28181
  return {
28180
28182
  type: MOVE_LINE,
28181
- lineIndex,
28183
+ itemIndex,
28182
28184
  delta
28183
28185
  };
28184
28186
  }
28187
+ /** This action assumes a flat array of vectors */
28188
+ const moveAll = delta => ({
28189
+ type: MOVE_ALL,
28190
+ delta
28191
+ });
28192
+
28193
+ /**
28194
+ * 44 is touch best practice and AAA compliant for WCAG
28195
+ * https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
28196
+ */
28197
+ const TARGET_SIZE = 44;
28198
+
28199
+ // same as pointsFromNormalized in interactive-graph.tsx
28200
+ const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
28201
+ // maps over [x, y]
28202
+ coords.map((coord, i) => {
28203
+ const axisRange = range[i];
28204
+ if (noSnap) {
28205
+ return axisRange[0] + (axisRange[1] - axisRange[0]) * coord;
28206
+ }
28207
+ const axisStep = step[i];
28208
+ const nSteps = Math.floor((axisRange[1] - axisRange[0]) / axisStep);
28209
+ const tick = Math.round(coord * nSteps);
28210
+ return axisRange[0] + axisStep * tick;
28211
+ }));
28212
+
28213
+ // same as normalizeCoords in interactive-graph.tsx
28214
+ const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
28215
+ const extent = ranges[i][1] - ranges[i][0];
28216
+ return (coord + ranges[i][1]) / extent;
28217
+ }));
28185
28218
 
28186
28219
  const defaultStroke = "var(--movable-line-stroke-color)";
28187
28220
  const MovableLine = props => {
@@ -28229,7 +28262,7 @@ const MovableLine = props => {
28229
28262
  end: endPtPx,
28230
28263
  style: {
28231
28264
  stroke: "transparent",
28232
- strokeWidth: 44
28265
+ strokeWidth: TARGET_SIZE
28233
28266
  }
28234
28267
  }), /*#__PURE__*/React.createElement(SVGLine, {
28235
28268
  start: startPtPx,
@@ -28262,7 +28295,7 @@ function SVGLine(props) {
28262
28295
  x2: end[0],
28263
28296
  y2: end[1],
28264
28297
  style: style,
28265
- className: dragging ? "movable-line-dragging" : undefined
28298
+ className: dragging ? "movable-dragging" : undefined
28266
28299
  });
28267
28300
  }
28268
28301
 
@@ -28337,7 +28370,7 @@ const StyledMovablePoint = props => {
28337
28370
  }
28338
28371
  }, /*#__PURE__*/React.createElement("circle", {
28339
28372
  className: "movable-point-hitbox",
28340
- r: 22,
28373
+ r: TARGET_SIZE / 2,
28341
28374
  cx: x,
28342
28375
  cy: y
28343
28376
  }), /*#__PURE__*/React.createElement("circle", {
@@ -28373,9 +28406,9 @@ const SegmentGraph = props => {
28373
28406
  snaps: snapStep,
28374
28407
  range: range,
28375
28408
  onMoveLine: delta => {
28376
- dispatch(moveSegment(i, delta));
28409
+ dispatch(moveLine(i, delta));
28377
28410
  },
28378
- onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(i, endpointIndex, destination)),
28411
+ onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
28379
28412
  "data-testid": "segment" + i
28380
28413
  })));
28381
28414
  };
@@ -28418,9 +28451,9 @@ const LinearGraph = props => {
28418
28451
  snaps: snapStep,
28419
28452
  range: range,
28420
28453
  onMoveLine: delta => {
28421
- dispatch(moveSegment(i, delta));
28454
+ dispatch(moveLine(i, delta));
28422
28455
  },
28423
- onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(i, endpointIndex, destination))
28456
+ onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i))
28424
28457
  // "linear" or "linear-system" + index
28425
28458
  ,
28426
28459
  "data-testid": type + i,
@@ -28471,8 +28504,8 @@ const RayGraph = props => {
28471
28504
 
28472
28505
  // a ray only has one line
28473
28506
  const [start, end] = lines[0];
28474
- const handleMoveLine = delta => dispatch(moveSegment(0, delta));
28475
- const handleMovePoint = (newPoint, pointIndex) => dispatch(moveControlPoint(0, pointIndex, newPoint));
28507
+ const handleMoveLine = delta => dispatch(moveLine(0, delta));
28508
+ const handleMovePoint = (newPoint, pointIndex) => dispatch(moveControlPoint(pointIndex, newPoint, 0));
28476
28509
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(MovableLine, {
28477
28510
  start: start,
28478
28511
  end: end,
@@ -28491,6 +28524,63 @@ const RayGraph = props => {
28491
28524
  }));
28492
28525
  };
28493
28526
 
28527
+ const PolygonGraph = props => {
28528
+ var _points$length;
28529
+ const [focused, setFocused] = React.useState(false);
28530
+ const [hovered, setHovered] = React.useState(false);
28531
+ const {
28532
+ dispatch
28533
+ } = props;
28534
+ const {
28535
+ coords,
28536
+ type
28537
+ } = props.graphState;
28538
+ const points = coords != null ? coords : [[0, 0]];
28539
+ const pointsSum = points.reduce((acc, point) => vec.add(acc, point), [0, 0]);
28540
+ const midpoint = pointsSum && vec.scale(pointsSum, 1 / ((_points$length = points.length) != null ? _points$length : 1));
28541
+ const ref = React.useRef(null);
28542
+ const {
28543
+ dragging
28544
+ } = useMovable({
28545
+ gestureTarget: ref,
28546
+ point: midpoint,
28547
+ onMove: newPoint => {
28548
+ const delta = vec.sub(newPoint, midpoint);
28549
+ dispatch(moveAll(delta));
28550
+ },
28551
+ constrain: p => p
28552
+ });
28553
+ const active = hovered || focused || dragging;
28554
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Polygon, {
28555
+ points: [...points],
28556
+ color: "var(--movable-line-stroke-color)",
28557
+ svgPolygonProps: {
28558
+ strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)"
28559
+ }
28560
+ }), /*#__PURE__*/React.createElement(Polygon, {
28561
+ points: [...points],
28562
+ color: "transparent",
28563
+ svgPolygonProps: {
28564
+ ref,
28565
+ tabIndex: 0,
28566
+ strokeWidth: TARGET_SIZE,
28567
+ style: {
28568
+ cursor: dragging ? "grabbing" : "grab"
28569
+ },
28570
+ onFocus: () => setFocused(true),
28571
+ onBlur: () => setFocused(false),
28572
+ onMouseEnter: () => setHovered(true),
28573
+ onMouseLeave: () => setHovered(false),
28574
+ className: "movable-polygon"
28575
+ }
28576
+ }), points.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
28577
+ key: i,
28578
+ point: point,
28579
+ onMove: destination => dispatch(moveControlPoint(i, destination)),
28580
+ "data-testid": type + i
28581
+ })));
28582
+ };
28583
+
28494
28584
  const renderLineLabel = (n, [min, max]) => n !== -1 && n !== min && n !== max;
28495
28585
  const axisOptions = (props, index) => ({
28496
28586
  axis: props.markings === "graph",
@@ -28503,233 +28593,6 @@ const Grid = props => props.markings === "none" ? null : /*#__PURE__*/React.crea
28503
28593
  yAxis: axisOptions(props, 1)
28504
28594
  });
28505
28595
 
28506
- function interactiveGraphReducer(state, action) {
28507
- switch (action.type) {
28508
- case MOVE_CONTROL_POINT:
28509
- {
28510
- const newCoords = updateAtIndex({
28511
- array: state.coords,
28512
- index: action.objectIndex,
28513
- update: tuple => setAtIndex({
28514
- array: tuple,
28515
- index: action.pointIndex,
28516
- newValue: snap(state, bound$1(state, action.destination))
28517
- })
28518
- });
28519
- if (!validSegments(newCoords)) {
28520
- return state;
28521
- }
28522
- return _extends({}, state, {
28523
- hasBeenInteractedWith: true,
28524
- coords: newCoords
28525
- });
28526
- }
28527
- case MOVE_LINE:
28528
- {
28529
- var _state$coords;
28530
- const currentLine = (_state$coords = state.coords) == null ? void 0 : _state$coords[action.lineIndex];
28531
- if (!currentLine) {
28532
- throw new Error("No line to move");
28533
- }
28534
- const maxMoves = currentLine.map(point => maxMove(state, point));
28535
- const minMoves = currentLine.map(point => minMove(state, point));
28536
- const maxXMove = Math.min(...maxMoves.map(move => move[0]));
28537
- const maxYMove = Math.min(...maxMoves.map(move => move[1]));
28538
- const minXMove = Math.max(...minMoves.map(move => move[0]));
28539
- const minYMove = Math.max(...minMoves.map(move => move[1]));
28540
- const dx = clamp(action.delta[0], minXMove, maxXMove);
28541
- const dy = clamp(action.delta[1], minYMove, maxYMove);
28542
- const newValue = [snap(state, vec.add(currentLine[0], [dx, dy])), snap(state, vec.add(currentLine[1], [dx, dy]))];
28543
- const newLine = setAtIndex({
28544
- array: state.coords,
28545
- index: action.lineIndex,
28546
- newValue
28547
- });
28548
- return _extends({}, state, {
28549
- hasBeenInteractedWith: true,
28550
- coords: newLine
28551
- });
28552
- }
28553
- default:
28554
- throw new UnreachableCaseError(action);
28555
- }
28556
- }
28557
- function snap(state, point) {
28558
- const [requestedX, requestedY] = point;
28559
- const [snapX, snapY] = state.snapStep;
28560
- return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
28561
- }
28562
-
28563
- // Returns the closest point to the given `point` that is within the graph
28564
- // bounds given in `state`.
28565
- function bound$1(state, point) {
28566
- const [requestedX, requestedY] = point;
28567
- const [snapX, snapY] = state.snapStep;
28568
- const [[minX, maxX], [minY, maxY]] = state.range;
28569
- return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
28570
- }
28571
-
28572
- // Returns the vector from the given point to the top-right corner of the graph
28573
- function maxMove(state, point) {
28574
- const topRight = bound$1(state, [Infinity, Infinity]);
28575
- return vec.sub(topRight, point);
28576
- }
28577
-
28578
- // Returns the vector from the given point to the bottom-left corner of the
28579
- // graph
28580
- function minMove(state, point) {
28581
- const bottomLeft = bound$1(state, [-Infinity, -Infinity]);
28582
- return vec.sub(bottomLeft, point);
28583
- }
28584
- function clamp(value, min, max) {
28585
- if (value < min) {
28586
- return min;
28587
- }
28588
- if (value > max) {
28589
- return max;
28590
- }
28591
- return value;
28592
- }
28593
- function validSegments(segments) {
28594
- return segments.every(([start, end]) => !vector$1.equal(start, end));
28595
- }
28596
- function updateAtIndex(args) {
28597
- const {
28598
- array = [],
28599
- index,
28600
- update
28601
- } = args;
28602
- const newValue = update(array[index]);
28603
- return setAtIndex({
28604
- array,
28605
- index,
28606
- newValue
28607
- });
28608
- }
28609
- function setAtIndex(args) {
28610
- const {
28611
- array = [],
28612
- index,
28613
- newValue
28614
- } = args;
28615
- const copy = [...array];
28616
- copy[index] = newValue;
28617
- // restoring readonly to array
28618
- return copy;
28619
- }
28620
-
28621
- // same as pointsFromNormalized in interactive-graph.tsx
28622
- const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
28623
- // maps over [x, y]
28624
- coords.map((coord, i) => {
28625
- const axisRange = range[i];
28626
- if (noSnap) {
28627
- return axisRange[0] + (axisRange[1] - axisRange[0]) * coord;
28628
- }
28629
- const axisStep = step[i];
28630
- const nSteps = Math.floor((axisRange[1] - axisRange[0]) / axisStep);
28631
- const tick = Math.round(coord * nSteps);
28632
- return axisRange[0] + axisStep * tick;
28633
- }));
28634
-
28635
- // same as normalizeCoords in interactive-graph.tsx
28636
- const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
28637
- const extent = ranges[i][1] - ranges[i][0];
28638
- return (coord + ranges[i][1]) / extent;
28639
- }));
28640
-
28641
- function initializeGraphState(params) {
28642
- const {
28643
- graph,
28644
- step,
28645
- snapStep,
28646
- range
28647
- } = params;
28648
- switch (graph.type) {
28649
- case "segment":
28650
- return {
28651
- type: "segment",
28652
- hasBeenInteractedWith: false,
28653
- range,
28654
- snapStep,
28655
- coords: getDefaultSegments({
28656
- graph,
28657
- step,
28658
- range
28659
- })
28660
- };
28661
- case "linear":
28662
- case "linear-system":
28663
- case "ray":
28664
- return {
28665
- type: graph.type,
28666
- hasBeenInteractedWith: false,
28667
- range,
28668
- snapStep,
28669
- // Linear and ray graphs have a single tuple of points, while a
28670
- // linear system has two tuples of points.
28671
- coords: getLineCoords(graph, range, step)
28672
- };
28673
- }
28674
- throw new Error("Mafs not yet implemented for graph type: " + graph.type);
28675
- }
28676
- const getDefaultSegments = props => {
28677
- const ys = n => {
28678
- switch (n) {
28679
- case 2:
28680
- return [5, -5];
28681
- case 3:
28682
- return [5, 0, -5];
28683
- case 4:
28684
- return [6, 2, -2, -6];
28685
- case 5:
28686
- return [6, 3, 0, -3, -6];
28687
- case 6:
28688
- return [5, 3, 1, -1, -3, -5];
28689
- default:
28690
- return [5];
28691
- }
28692
- };
28693
- return ys(props.graph.numSegments).map(y => {
28694
- let endpoints = [[-5, y], [5, y]];
28695
- endpoints = normalizeCoords(endpoints, props.range);
28696
- endpoints = normalizePoints(props.range, props.step, endpoints);
28697
- return endpoints;
28698
- });
28699
- };
28700
-
28701
- // TS v4 doesn't narrow return types, while v5 does.
28702
- // Instead of updating to v5, using generic type to relate input and output types.
28703
- function getGradableGraph(state, initialGraph) {
28704
- var _state$coords;
28705
- if (!state.hasBeenInteractedWith) {
28706
- return initialGraph;
28707
- }
28708
- switch (true) {
28709
- // coords: Array of CollinearTuple
28710
- case state.type === "linear-system" && initialGraph.type === "linear-system":
28711
- case state.type === "segment" && initialGraph.type === "segment":
28712
- return _extends({}, initialGraph, {
28713
- coords: state.coords
28714
- });
28715
- // coords: CollinearTuple
28716
- case state.type === "linear" && initialGraph.type === "linear":
28717
- case state.type === "ray" && initialGraph.type === "ray":
28718
- return _extends({}, initialGraph, {
28719
- coords: (_state$coords = state.coords) == null ? void 0 : _state$coords[0]
28720
- });
28721
- }
28722
- throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
28723
- }
28724
- const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
28725
- const getLineCoords = (graph, range, step) => {
28726
- var _graph$coords;
28727
- return (
28728
- // Return two lines for a linear system, one for a ray or linear
28729
- ((_graph$coords = graph.coords) != null ? _graph$coords : graph.type === "linear-system") ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
28730
- );
28731
- };
28732
-
28733
28596
  // Displays a stylized open eye: lint warnings are visible
28734
28597
  const VisibleIcon = () => /*#__PURE__*/React.createElement("svg", {
28735
28598
  width: "24",
@@ -29422,6 +29285,373 @@ const getLegacyGrid = (box, backgroundImage) => {
29422
29285
  return null;
29423
29286
  };
29424
29287
 
29288
+ /** Determine if coords is type CollinearTuple[] */
29289
+ const isCollinearTuples = coords => Array.isArray(coords[0][0]);
29290
+
29291
+ // Generic type makes returned state match input state
29292
+ function interactiveGraphReducer(state, action) {
29293
+ const {
29294
+ snapStep,
29295
+ range
29296
+ } = state;
29297
+ if (!state.coords) {
29298
+ throw new Error("Graph state must have been initialized with coords");
29299
+ }
29300
+ switch (action.type) {
29301
+ case MOVE_CONTROL_POINT:
29302
+ {
29303
+ const newCoords = action.itemIndex !== undefined && state.coords && isCollinearTuples(state.coords) ? updateAtIndex({
29304
+ array: state.coords,
29305
+ index: action.itemIndex,
29306
+ update: tuple => setAtIndex({
29307
+ array: tuple,
29308
+ index: action.pointIndex,
29309
+ newValue: snap({
29310
+ snapStep,
29311
+ point: bound$1({
29312
+ snapStep,
29313
+ range,
29314
+ point: action.destination
29315
+ })
29316
+ })
29317
+ })
29318
+ }) : setAtIndex({
29319
+ array: state.coords,
29320
+ index: action.pointIndex,
29321
+ newValue: snap({
29322
+ snapStep,
29323
+ point: bound$1({
29324
+ snapStep,
29325
+ range,
29326
+ point: action.destination
29327
+ })
29328
+ })
29329
+ });
29330
+
29331
+ // Cannot type narrow both conditions within function parameters,
29332
+ // so this may seem redundant, but it's necessary for type safety.
29333
+ const coordsToCheck = isCollinearTuples(newCoords) && action.itemIndex !== undefined ? newCoords[action.itemIndex] : newCoords;
29334
+ if (!isCollinearTuples(coordsToCheck) && coordsOverlap(coordsToCheck)) {
29335
+ return state;
29336
+ }
29337
+ return _extends({}, state, {
29338
+ hasBeenInteractedWith: true,
29339
+ coords: newCoords
29340
+ });
29341
+ }
29342
+ case MOVE_LINE:
29343
+ {
29344
+ var _state$coords;
29345
+ if (!isCollinearTuples(state.coords)) {
29346
+ throw new Error("Cannot call this action unless coords is array of CollinearTuple.");
29347
+ }
29348
+ if (action.itemIndex === undefined) {
29349
+ throw new Error("Please provide index of line to move");
29350
+ }
29351
+ const currentLine = (_state$coords = state.coords) == null ? void 0 : _state$coords[action.itemIndex];
29352
+ if (!currentLine) {
29353
+ throw new Error("No line to move");
29354
+ }
29355
+ const change = getChange(currentLine, action.delta, {
29356
+ snapStep,
29357
+ range
29358
+ });
29359
+ const newLine = [snap({
29360
+ snapStep,
29361
+ point: vec.add(currentLine[0], change)
29362
+ }), snap({
29363
+ snapStep,
29364
+ point: vec.add(currentLine[1], change)
29365
+ })];
29366
+ const newCoords = setAtIndex({
29367
+ array: state.coords,
29368
+ index: action.itemIndex,
29369
+ newValue: newLine
29370
+ });
29371
+ return _extends({}, state, {
29372
+ hasBeenInteractedWith: true,
29373
+ coords: newCoords
29374
+ });
29375
+ }
29376
+ case MOVE_ALL:
29377
+ {
29378
+ if (isCollinearTuples(state.coords)) {
29379
+ throw new Error("Cannot call this action unless coords is array of vectors.");
29380
+ }
29381
+ const change = getChange(state.coords, action.delta, {
29382
+ snapStep,
29383
+ range
29384
+ });
29385
+ const newCoords = state.coords.map(point => snap({
29386
+ snapStep,
29387
+ point: vec.add(point, change)
29388
+ }));
29389
+ return _extends({}, state, {
29390
+ hasBeenInteractedWith: true,
29391
+ coords: newCoords
29392
+ });
29393
+ }
29394
+ default:
29395
+ throw new UnreachableCaseError(action);
29396
+ }
29397
+ }
29398
+ const getChange = (coords, delta, constraintOpts) => {
29399
+ const [deltaX, deltaY] = delta;
29400
+ const maxMoves = coords.map(point => maxMove(_extends({}, constraintOpts, {
29401
+ point
29402
+ })));
29403
+ const minMoves = coords.map(point => minMove(_extends({}, constraintOpts, {
29404
+ point
29405
+ })));
29406
+ const maxXMove = Math.min(...maxMoves.map(move => move[0]));
29407
+ const maxYMove = Math.min(...maxMoves.map(move => move[1]));
29408
+ const minXMove = Math.max(...minMoves.map(move => move[0]));
29409
+ const minYMove = Math.max(...minMoves.map(move => move[1]));
29410
+ const dx = clamp(deltaX, minXMove, maxXMove);
29411
+ const dy = clamp(deltaY, minYMove, maxYMove);
29412
+ return [dx, dy];
29413
+ };
29414
+ function snap({
29415
+ snapStep,
29416
+ point
29417
+ }) {
29418
+ const [requestedX, requestedY] = point;
29419
+ const [snapX, snapY] = snapStep;
29420
+ return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
29421
+ }
29422
+
29423
+ // Returns the closest point to the given `point` that is within the graph
29424
+ // bounds given in `state`.
29425
+ function bound$1({
29426
+ snapStep,
29427
+ range,
29428
+ point
29429
+ }) {
29430
+ const [requestedX, requestedY] = point;
29431
+ const [snapX, snapY] = snapStep;
29432
+ const [[minX, maxX], [minY, maxY]] = range;
29433
+ return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
29434
+ }
29435
+
29436
+ // Returns the vector from the given point to the top-right corner of the graph
29437
+ function maxMove({
29438
+ snapStep,
29439
+ range,
29440
+ point
29441
+ }) {
29442
+ const topRight = bound$1({
29443
+ snapStep,
29444
+ range,
29445
+ point: [Infinity, Infinity]
29446
+ });
29447
+ return vec.sub(topRight, point);
29448
+ }
29449
+
29450
+ // Returns the vector from the given point to the bottom-left corner of the
29451
+ // graph
29452
+ function minMove({
29453
+ snapStep,
29454
+ range,
29455
+ point
29456
+ }) {
29457
+ const bottomLeft = bound$1({
29458
+ snapStep,
29459
+ range,
29460
+ point: [-Infinity, -Infinity]
29461
+ });
29462
+ return vec.sub(bottomLeft, point);
29463
+ }
29464
+ function clamp(value, min, max) {
29465
+ if (value < min) {
29466
+ return min;
29467
+ }
29468
+ if (value > max) {
29469
+ return max;
29470
+ }
29471
+ return value;
29472
+ }
29473
+ const coordsOverlap = coords => coords.some((coord, i) => coords.some((c, j) => i !== j && vector$1.equal(coord, c)));
29474
+ function updateAtIndex(args) {
29475
+ const {
29476
+ array = [],
29477
+ index,
29478
+ update
29479
+ } = args;
29480
+ const newValue = update(array[index]);
29481
+ return setAtIndex({
29482
+ array,
29483
+ index,
29484
+ newValue
29485
+ });
29486
+ }
29487
+ function setAtIndex(args) {
29488
+ const {
29489
+ array = [],
29490
+ index,
29491
+ newValue
29492
+ } = args;
29493
+ const copy = [...array];
29494
+ copy[index] = newValue;
29495
+ // restoring readonly to array
29496
+ return copy;
29497
+ }
29498
+
29499
+ function initializeGraphState(params) {
29500
+ const {
29501
+ graph,
29502
+ step,
29503
+ snapStep,
29504
+ range
29505
+ } = params;
29506
+ switch (graph.type) {
29507
+ case "segment":
29508
+ return {
29509
+ type: "segment",
29510
+ hasBeenInteractedWith: false,
29511
+ range,
29512
+ snapStep,
29513
+ coords: getDefaultSegments({
29514
+ graph,
29515
+ step,
29516
+ range
29517
+ })
29518
+ };
29519
+ case "linear":
29520
+ case "linear-system":
29521
+ case "ray":
29522
+ return {
29523
+ type: graph.type,
29524
+ hasBeenInteractedWith: false,
29525
+ range,
29526
+ snapStep,
29527
+ // Linear and ray graphs have a single tuple of points, while a
29528
+ // linear system has two tuples of points.
29529
+ coords: getLineCoords({
29530
+ graph,
29531
+ range,
29532
+ step
29533
+ })
29534
+ };
29535
+ case "polygon":
29536
+ return {
29537
+ type: "polygon",
29538
+ hasBeenInteractedWith: false,
29539
+ range,
29540
+ snapStep,
29541
+ coords: getPolygonCoords({
29542
+ graph,
29543
+ range,
29544
+ step
29545
+ })
29546
+ };
29547
+ case "angle":
29548
+ case "point":
29549
+ case "circle":
29550
+ case "sinusoid":
29551
+ case "quadratic":
29552
+ throw new Error("Mafs not yet implemented for graph type: " + graph.type);
29553
+ default:
29554
+ throw new UnreachableCaseError(graph);
29555
+ }
29556
+ }
29557
+
29558
+ // TS v4 doesn't narrow return types, while v5 does.
29559
+ // Instead of updating to v5, using generic type to relate input and output types.
29560
+ function getGradableGraph(state, initialGraph) {
29561
+ var _state$coords;
29562
+ if (!state.hasBeenInteractedWith) {
29563
+ return initialGraph;
29564
+ }
29565
+ switch (true) {
29566
+ // coords: Array of CollinearTuple
29567
+ case state.type === "linear-system" && initialGraph.type === "linear-system":
29568
+ case state.type === "segment" && initialGraph.type === "segment":
29569
+ return _extends({}, initialGraph, {
29570
+ coords: state.coords
29571
+ });
29572
+ // coords: CollinearTuple
29573
+ case state.type === "linear" && initialGraph.type === "linear":
29574
+ case state.type === "ray" && initialGraph.type === "ray":
29575
+ return _extends({}, initialGraph, {
29576
+ coords: (_state$coords = state.coords) == null ? void 0 : _state$coords[0]
29577
+ });
29578
+ case state.type === "polygon" && initialGraph.type === "polygon":
29579
+ return _extends({}, initialGraph, {
29580
+ coords: state.coords
29581
+ });
29582
+ default:
29583
+ throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
29584
+ }
29585
+ }
29586
+ const getDefaultSegments = ({
29587
+ graph,
29588
+ range,
29589
+ step
29590
+ }) => {
29591
+ const ys = n => {
29592
+ switch (n) {
29593
+ case 2:
29594
+ return [5, -5];
29595
+ case 3:
29596
+ return [5, 0, -5];
29597
+ case 4:
29598
+ return [6, 2, -2, -6];
29599
+ case 5:
29600
+ return [6, 3, 0, -3, -6];
29601
+ case 6:
29602
+ return [5, 3, 1, -1, -3, -5];
29603
+ default:
29604
+ return [5];
29605
+ }
29606
+ };
29607
+ return ys(graph.numSegments).map(y => {
29608
+ let endpoints = [[-5, y], [5, y]];
29609
+ endpoints = normalizeCoords(endpoints, range);
29610
+ endpoints = normalizePoints(range, step, endpoints);
29611
+ return endpoints;
29612
+ });
29613
+ };
29614
+ const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
29615
+ const getLineCoords = ({
29616
+ graph,
29617
+ range,
29618
+ step
29619
+ }) => {
29620
+ var _graph$coords;
29621
+ return (
29622
+ // Return two lines for a linear system, one for a ray or linear
29623
+ ((_graph$coords = graph.coords) != null ? _graph$coords : graph.type === "linear-system") ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
29624
+ );
29625
+ };
29626
+ const getPolygonCoords = ({
29627
+ graph,
29628
+ range,
29629
+ step
29630
+ }) => {
29631
+ let coords = graph.coords;
29632
+ if (coords) {
29633
+ return coords;
29634
+ }
29635
+ const n = graph.numSides || 3;
29636
+ if (n === "unlimited") {
29637
+ coords = [];
29638
+ } else {
29639
+ const angle = 2 * Math.PI / n;
29640
+ const offset = (1 / n - 1 / 2) * Math.PI;
29641
+
29642
+ // TODO(alex): Generalize this to more than just triangles so that
29643
+ // all polygons have whole number side lengths if snapping to sides
29644
+ const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
29645
+
29646
+ // Generate coords of a regular polygon with n sides
29647
+ coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
29648
+ }
29649
+ coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
29650
+ const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
29651
+ coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
29652
+ return coords;
29653
+ };
29654
+
29425
29655
  const renderGraph = props => {
29426
29656
  const {
29427
29657
  state,
@@ -29447,12 +29677,16 @@ const renderGraph = props => {
29447
29677
  graphState: state,
29448
29678
  dispatch: dispatch
29449
29679
  });
29680
+ case "polygon":
29681
+ return /*#__PURE__*/React.createElement(PolygonGraph, {
29682
+ graphState: state,
29683
+ dispatch: dispatch
29684
+ });
29450
29685
  default:
29451
29686
  return new UnreachableCaseError(type);
29452
29687
  }
29453
29688
  };
29454
29689
  const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
29455
- var _props$backgroundImag, _props$backgroundImag2, _props$backgroundImag3, _props$backgroundImag4;
29456
29690
  const [width, height] = props.box;
29457
29691
  const legacyGrid = getLegacyGrid([width, height], props.backgroundImage);
29458
29692
  const [state, dispatch] = React.useReducer(interactiveGraphReducer, props, initializeGraphState);
@@ -29461,8 +29695,8 @@ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
29461
29695
  }));
29462
29696
  return /*#__PURE__*/React.createElement(View, {
29463
29697
  style: {
29464
- width: (_props$backgroundImag = (_props$backgroundImag2 = props.backgroundImage) == null ? void 0 : _props$backgroundImag2.width) != null ? _props$backgroundImag : width,
29465
- height: (_props$backgroundImag3 = (_props$backgroundImag4 = props.backgroundImage) == null ? void 0 : _props$backgroundImag4.height) != null ? _props$backgroundImag3 : height,
29698
+ width,
29699
+ height,
29466
29700
  position: "relative"
29467
29701
  }
29468
29702
  }, legacyGrid, /*#__PURE__*/React.createElement(View, {
@@ -41529,7 +41763,7 @@ const init = function init(options) {
41529
41763
 
41530
41764
  // This file is processed by a Rollup plugin (replace) to inject the production
41531
41765
  const libName = "@khanacademy/perseus";
41532
- const libVersion = "20.6.0";
41766
+ const libVersion = "20.8.0";
41533
41767
  addLibraryVersionToPerseusDebug(libName, libVersion);
41534
41768
 
41535
41769
  const apiVersion = {
@@ -44186,6 +44420,66 @@ function injectWidgets(content, widgets) {
44186
44420
  return context;
44187
44421
  }
44188
44422
 
44423
+ /* Widgets that have individual answers */
44424
+ const INDIVIDUAL_ANSWER_WIDGETS = ["interactive-graph", "categorizer", "grapher"];
44425
+
44426
+ /* Widgets that are supported for automatic scoring */
44427
+ const SUPPORTED_WIDGETS = ["radio", "numeric-input", "input-number", "expression", ...INDIVIDUAL_ANSWER_WIDGETS];
44428
+ const getWidgetTypeFromWidgetKey = widgetKey => {
44429
+ return widgetKey.split(" ")[0];
44430
+ };
44431
+
44432
+ /* Verify if the perseus item has supported widgets for automatic scoring */
44433
+ const isWrongAnswerSupported = widgetKeys => {
44434
+ return widgetKeys.length !== 0 && widgetKeys.every(widgetKey => SUPPORTED_WIDGETS.includes(getWidgetTypeFromWidgetKey(widgetKey)));
44435
+ };
44436
+
44437
+ /* Verify if the widget key has an individual answer for the coach report view */
44438
+ const shouldHaveIndividualAnswer = widgetKey => {
44439
+ return INDIVIDUAL_ANSWER_WIDGETS.includes(getWidgetTypeFromWidgetKey(widgetKey));
44440
+ };
44441
+
44442
+ /* Returns the answer userInput submission for currently supported widgets */
44443
+ // TODO (LEMS-1834): Fix userInput any types to be specific
44444
+ const getAnswerFromUserInput = (widgetType, userInput) => {
44445
+ switch (widgetType) {
44446
+ case "categorizer":
44447
+ return userInput.values;
44448
+ case "input-number":
44449
+ return userInput.currentValue;
44450
+ case "numeric-input":
44451
+ return userInput.currentValue;
44452
+ case "radio":
44453
+ return userInput.choicesSelected;
44454
+ }
44455
+ return userInput;
44456
+ };
44457
+
44458
+ /* Returns the correct answer for a given widget key and Perseus Item */
44459
+ // TODO (LEMS-1835): We should fix the resonse type from getWidget to be specific.
44460
+ // TODO (LEMS-1836): We should also consider adding the getOneCorrectAnswerFromRubric method to all widgets.
44461
+ const getCorrectAnswerForWidgetKey = (widgetKey, itemData) => {
44462
+ const rubric = itemData.question.widgets[widgetKey].options;
44463
+ const widgetType = getWidgetTypeFromWidgetKey(widgetKey);
44464
+ const widget = getWidget(widgetType);
44465
+
44466
+ // @ts-expect-error - TS2339 - Property 'getOneCorrectAnswerFromRubric' does not exist on type 'ComponentType<any>'.
44467
+ return widget == null || widget.getOneCorrectAnswerFromRubric == null ? void 0 : widget.getOneCorrectAnswerFromRubric(rubric);
44468
+ };
44469
+
44470
+ /* Verify if the widget key exists in the content string of the Perseus Item */
44471
+ const isWidgetKeyInContent = (perseusItem, widgetKey) => {
44472
+ return perseusItem.question.content.indexOf(widgetKey) !== -1;
44473
+ };
44474
+
44475
+ /* Return an array of all the widget keys that exist in the content string of a Perseus Item */
44476
+ const getValidWidgetKeys = perseusItem => {
44477
+ const {
44478
+ widgets
44479
+ } = perseusItem.question;
44480
+ return keys(widgets).filter(key => isWidgetKeyInContent(perseusItem, key));
44481
+ };
44482
+
44189
44483
  /**
44190
44484
  * Helper to parse PerseusItem JSON
44191
44485
  * Why not just use JSON.parse? We want:
@@ -44302,6 +44596,153 @@ function getWidgetTypeFromWidgetId(widgetId) {
44302
44596
  return widgetType;
44303
44597
  }
44304
44598
 
44599
+ let WidgetType = /*#__PURE__*/function (WidgetType) {
44600
+ WidgetType["Categorizer"] = "categorizer";
44601
+ WidgetType["CSProgram"] = "cs-program";
44602
+ WidgetType["Definition"] = "definition";
44603
+ WidgetType["Dropdown"] = "dropdown";
44604
+ WidgetType["Explanation"] = "explanation";
44605
+ WidgetType["Expression"] = "expression";
44606
+ WidgetType["GradedGroup"] = "graded-group";
44607
+ WidgetType["GradedGroupAnswerBar"] = "graded-group-answer-bar";
44608
+ WidgetType["GradedGroupSet"] = "graded-group-set";
44609
+ WidgetType["Grapher"] = "grapher";
44610
+ WidgetType["Group"] = "group";
44611
+ WidgetType["Iframe"] = "iframe";
44612
+ WidgetType["Image"] = "image";
44613
+ WidgetType["InputNumber"] = "input-number";
44614
+ WidgetType["Interaction"] = "interaction";
44615
+ WidgetType["InteractiveGraph"] = "interactive-graph";
44616
+ WidgetType["InteractiveNumberLine"] = "interactive-number-line";
44617
+ WidgetType["LabelImage"] = "label-image";
44618
+ WidgetType["Matcher"] = "matcher";
44619
+ WidgetType["Matrix"] = "matrix";
44620
+ WidgetType["Measurer"] = "measurer";
44621
+ WidgetType["Molecule"] = "molecule";
44622
+ WidgetType["NumberLine"] = "number-line";
44623
+ WidgetType["NumericInput"] = "numeric-input";
44624
+ WidgetType["Orderer"] = "orderer";
44625
+ WidgetType["Passage"] = "passage";
44626
+ WidgetType["PassageRef"] = "passage-ref";
44627
+ WidgetType["PassageRefTarget"] = "passage-ref-target";
44628
+ WidgetType["Plotter"] = "plotter";
44629
+ WidgetType["Protractor"] = "protractor";
44630
+ WidgetType["PythonProgram"] = "python-program";
44631
+ WidgetType["Radio"] = "radio";
44632
+ WidgetType["Sorter"] = "sorter";
44633
+ WidgetType["Table"] = "table";
44634
+ WidgetType["Unit"] = "unit";
44635
+ WidgetType["Video"] = "video";
44636
+ WidgetType["VideoTranscriptLink"] = "video-transcript-link";
44637
+ return WidgetType;
44638
+ }({});
44639
+ // These are all the current widgets that require user-input and are supported for automatic scoring
44640
+ const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
44641
+
44642
+ /**
44643
+ * Regex for widget placeholders in a string.
44644
+ *
44645
+ * First capture group is the widget ID (ex. 'radio 1')
44646
+ * Second capture group is the widget type (ex. "radio)
44647
+ * exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
44648
+ */
44649
+ function getWidgetRegex() {
44650
+ return /\[\[☃ (([a-z-]+) \d+)\]\]/g;
44651
+ }
44652
+
44653
+ /**
44654
+ * Add a widget placeholder using the provided widget type and instance number.
44655
+ * ex. addWidget(WidgetType.Radio, 1) => "[[☃ radio 1]]"
44656
+ *
44657
+ * @param {WidgetType} widgetType
44658
+ * @param {number} instance
44659
+ * @returns {string}
44660
+ */
44661
+ function addWidget(widgetType, instance) {
44662
+ return `[[☃ ${widgetType} ${String(instance)}]]`;
44663
+ }
44664
+
44665
+ /**
44666
+ * Extract all widget IDs, which includes the widget type and instance number.
44667
+ * example output: ['radio 1', 'categorizer 1', 'categorizor 2']
44668
+ *
44669
+ * Content should contain Perseus widget placeholders,
44670
+ * which look like: '[[☃ radio 1]]'.
44671
+ *
44672
+ * @param {string} content
44673
+ * @returns {Array<WidgetId>} widgets
44674
+ */
44675
+ function getAllWidgetIds(content) {
44676
+ const widgets = [];
44677
+ const localWidgetRegex = getWidgetRegex();
44678
+ let match = localWidgetRegex.exec(content);
44679
+ while (match !== null) {
44680
+ widgets.push(match[1]);
44681
+ match = localWidgetRegex.exec(content);
44682
+ }
44683
+ return widgets;
44684
+ }
44685
+
44686
+ /**
44687
+ * Check if a specific widget is an actual widget type using its name.
44688
+ * The widget name should include a widget type, without an instance number,
44689
+ * as a string. ex. 'radio'
44690
+ *
44691
+ * @param {string} widgetName
44692
+ * @returns {boolean}
44693
+ */
44694
+ function isWidgetType(widgetName) {
44695
+ return Object.values(WidgetType).includes(widgetName);
44696
+ }
44697
+
44698
+ /**
44699
+ * Extract all widget types from a Perseus JSON content string.
44700
+ * This does not include the instance number and prevents duplicates.
44701
+ * example output: ['radio', 'categorizer']
44702
+ *
44703
+ * @param {string} content
44704
+ * @returns {Array<WidgetId>}
44705
+ */
44706
+ function getAllWidgetTypes(content) {
44707
+ const widgetTypes = [];
44708
+ const localWidgetRegex = getWidgetRegex();
44709
+ let match = localWidgetRegex.exec(content);
44710
+ while (match !== null) {
44711
+ const matchType = match[2];
44712
+ if (!widgetTypes.includes(matchType) && isWidgetType(matchType)) {
44713
+ widgetTypes.push(matchType);
44714
+ }
44715
+ match = localWidgetRegex.exec(content);
44716
+ }
44717
+ return widgetTypes;
44718
+ }
44719
+
44720
+ /**
44721
+ * Check if a specific widget is a question widget type using its ID.
44722
+ * The widget ID includes the widget type and the instance number.
44723
+ *
44724
+ * @param {WidgetId} widgetId
44725
+ * @returns {boolean}
44726
+ */
44727
+ function isQuestionWidgetType(widgetId) {
44728
+ return QUESTION_WIDGETS.includes(widgetId.split(" ")[0]);
44729
+ }
44730
+
44731
+ /**
44732
+ * Extract the widget IDs of all widgets considered question widgets. Widget IDs
44733
+ * include widget type and instance number.
44734
+ * example output: ['radio 1', 'categorizer 1']
44735
+ *
44736
+ * Content should contain Perseus widget placeholders,
44737
+ * which look like: '[[☃ radio 1]]'.
44738
+ *
44739
+ * @param {string} content
44740
+ * @returns Array<WidgetId>
44741
+ */
44742
+ function getQuestionWidgetIds(content) {
44743
+ return getAllWidgetIds(content).filter(isQuestionWidgetType);
44744
+ }
44745
+
44305
44746
  const EditorJsonify = {
44306
44747
  serialize: function () {
44307
44748
  // Omit props that get passed to all widgets
@@ -44310,5 +44751,5 @@ const EditorJsonify = {
44310
44751
  }
44311
44752
  };
44312
44753
 
44313
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, convertWidgetNameToEnum, displaySigFigs, generateTestPerseusItem, genericPerseusItemData, getAnswersFromWidgets, getWidgetTypeFromWidgetId, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, traverse, violatingWidgets, allWidgets as widgets };
44754
+ export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, WidgetType, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, convertWidgetNameToEnum, displaySigFigs, generateTestPerseusItem, genericPerseusItemData, getAllWidgetIds, getAllWidgetTypes, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetKey, getQuestionWidgetIds, getValidWidgetKeys, getWidgetTypeFromWidgetId, getWidgetTypeFromWidgetKey, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetKeyInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, violatingWidgets, allWidgets as widgets };
44314
44755
  //# sourceMappingURL=index.js.map