@khanacademy/perseus 37.0.0 → 38.0.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.
Files changed (55) hide show
  1. package/dist/all-widgets.d.ts +1 -0
  2. package/dist/basic-widgets.d.ts +1 -0
  3. package/dist/es/index.js +134 -114
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/extra-widgets.d.ts +1 -0
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +131 -112
  8. package/dist/index.js.map +1 -1
  9. package/dist/multi-items/item-types.d.ts +4 -3
  10. package/dist/perseus-api.d.ts +7 -6
  11. package/dist/perseus-types.d.ts +31 -30
  12. package/dist/server-item-renderer.d.ts +8 -9
  13. package/dist/types.d.ts +3 -5
  14. package/dist/util/extract-perseus-data.d.ts +1 -1
  15. package/dist/validation.types.d.ts +4 -11
  16. package/dist/widgets/categorizer/categorizer.d.ts +1 -0
  17. package/dist/widgets/cs-program/cs-program.d.ts +1 -0
  18. package/dist/widgets/definition/definition.d.ts +1 -0
  19. package/dist/widgets/deprecated-standin/deprecated-standin.d.ts +1 -0
  20. package/dist/widgets/dropdown/dropdown.d.ts +1 -0
  21. package/dist/widgets/explanation/explanation.d.ts +1 -0
  22. package/dist/widgets/expression/expression.d.ts +43 -37
  23. package/dist/widgets/graded-group/graded-group.d.ts +1 -0
  24. package/dist/widgets/graded-group-set/graded-group-set.d.ts +1 -0
  25. package/dist/widgets/grapher/grapher-types.d.ts +58 -0
  26. package/dist/widgets/grapher/grapher-validator.d.ts +1 -1
  27. package/dist/widgets/grapher/grapher.d.ts +1 -0
  28. package/dist/widgets/grapher/util.d.ts +16 -3
  29. package/dist/widgets/group/group.d.ts +1 -0
  30. package/dist/widgets/iframe/iframe.d.ts +1 -0
  31. package/dist/widgets/image/image.d.ts +1 -0
  32. package/dist/widgets/input-number/input-number.d.ts +1 -1
  33. package/dist/widgets/interaction/interaction.d.ts +1 -0
  34. package/dist/widgets/interactive-graph.d.ts +1 -0
  35. package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +1 -1
  36. package/dist/widgets/label-image/label-image.d.ts +15 -12
  37. package/dist/widgets/matcher/matcher.d.ts +1 -0
  38. package/dist/widgets/matrix/matrix.d.ts +1 -0
  39. package/dist/widgets/measurer/measurer.d.ts +1 -0
  40. package/dist/widgets/molecule/molecule.d.ts +1 -0
  41. package/dist/widgets/number-line/number-line.d.ts +1 -0
  42. package/dist/widgets/numeric-input/numeric-input.d.ts +1 -1
  43. package/dist/widgets/orderer/orderer.d.ts +1 -0
  44. package/dist/widgets/passage/passage.d.ts +1 -0
  45. package/dist/widgets/passage-ref/passage-ref.d.ts +1 -0
  46. package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +1 -0
  47. package/dist/widgets/phet-simulation/phet-simulation.d.ts +1 -0
  48. package/dist/widgets/plotter/plotter.d.ts +1 -0
  49. package/dist/widgets/python-program/python-program.d.ts +1 -0
  50. package/dist/widgets/radio/radio.d.ts +1 -0
  51. package/dist/widgets/sorter/sorter.d.ts +1 -0
  52. package/dist/widgets/table/table.d.ts +1 -0
  53. package/dist/widgets/video/video.d.ts +1 -0
  54. package/dist/widgets.d.ts +1 -0
  55. package/package.json +1 -1
@@ -16,6 +16,7 @@ declare const _default: readonly Readonly<{
16
16
  transform?: import("./types").WidgetTransform | undefined;
17
17
  staticTransform?: import("./types").WidgetTransform | undefined;
18
18
  validator?: import("./types").WidgetValidatorFunction | undefined;
19
+ getOneCorrectAnswerFromRubric?: ((rubric: import("./validation.types").Rubric) => string | null | undefined) | undefined;
19
20
  propUpgrades?: {
20
21
  [key: string]: (arg1: any) => any;
21
22
  } | undefined;
package/dist/index.d.ts CHANGED
@@ -82,7 +82,7 @@ export { default as WIDGET_PROP_DENYLIST } from "./mixins/widget-prop-denylist";
82
82
  export type { PerseusOptions } from "./init";
83
83
  export type { ILogger, LogErrorOptions } from "./logging/log";
84
84
  export type { ServerItemRenderer as ServerItemRendererComponent } from "./server-item-renderer";
85
- export type { Alignment, APIOptions, APIOptionsWithDefaults, ChangeHandler, DeviceType, DomInsertCheckFn, EditorMode, FocusPath, ImageDict, ImageUploader, JiptLabelStore, JiptRenderer, PerseusDependencies, PerseusScore, Version, VideoData, VideoKind, WidgetDict, WidgetExports, SharedRendererProps, } from "./types";
85
+ export type { Alignment, APIOptions, APIOptionsWithDefaults, ChangeHandler, DeviceType, DomInsertCheckFn, EditorMode, FocusPath, ImageDict, ImageUploader, JiptLabelStore, JiptRenderer, PerseusDependencies, PerseusScore, Version, VideoData, VideoKind, WidgetExports, SharedRendererProps, } from "./types";
86
86
  export type { ParsedValue } from "./util";
87
87
  export type { Hint, LegacyButtonSets, LockedFigure, LockedFigureColor, LockedFigureFillType, LockedFigureType, LockedPointType, LockedLineType, LockedVectorType, LockedEllipseType, LockedPolygonType, LockedFunctionType, LockedLabelType, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions, Range, Size, CollinearTuple, MathFormat, InputNumberWidget, // TODO(jeremy): remove?
88
88
  PerseusImageBackground, PerseusInputNumberWidgetOptions, PerseusInteractiveGraphWidgetOptions, PerseusItem, PerseusPhetSimulationWidgetOptions, PerseusPlotterWidgetOptions, PerseusPythonProgramWidgetOptions, PerseusRadioWidgetOptions, PerseusExampleWidgetOptions, PerseusSimpleMarkdownTesterWidgetOptions, PerseusRenderer, PerseusWidget, PerseusWidgetsMap, MultiItem, } from "./perseus-types";
package/dist/index.js CHANGED
@@ -2394,15 +2394,6 @@ class Expression extends React__namespace.Component {
2394
2394
  static getUserInputFromProps(props) {
2395
2395
  return normalizeTex(props.value);
2396
2396
  }
2397
- static getOneCorrectAnswerFromRubric(rubric) {
2398
- const correctAnswers = (rubric.answerForms || []).filter(answerForm => answerForm.considered === "correct");
2399
- if (correctAnswers.length === 0) {
2400
- return;
2401
- }
2402
- return correctAnswers[0].value;
2403
- }
2404
- //#endregion
2405
-
2406
2397
  static defaultProps = {
2407
2398
  value: "",
2408
2399
  times: false,
@@ -2728,11 +2719,7 @@ const ExpressionWithDependencies = /*#__PURE__*/React__namespace.forwardRef((pro
2728
2719
  // methods and instead adjust Perseus to provide these facilities through
2729
2720
  // instance methods on our Renderers.
2730
2721
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2731
- ExpressionWithDependencies.validate = expressionValidator;
2732
- // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2733
2722
  ExpressionWithDependencies.getUserInputFromProps = Expression.getUserInputFromProps;
2734
- // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2735
- ExpressionWithDependencies.getOneCorrectAnswerFromRubric = Expression.getOneCorrectAnswerFromRubric;
2736
2723
  var Expression$1 = {
2737
2724
  name: "expression",
2738
2725
  displayName: "Expression / Equation",
@@ -2765,7 +2752,14 @@ var Expression$1 = {
2765
2752
  propUpgrades: propUpgrades$2,
2766
2753
  // For use by the editor
2767
2754
  isLintable: true,
2768
- validator: expressionValidator
2755
+ validator: expressionValidator,
2756
+ getOneCorrectAnswerFromRubric(rubric) {
2757
+ const correctAnswers = (rubric.answerForms || []).filter(answerForm => answerForm.considered === "correct");
2758
+ if (correctAnswers.length === 0) {
2759
+ return;
2760
+ }
2761
+ return correctAnswers[0].value;
2762
+ }
2769
2763
  };
2770
2764
 
2771
2765
  /**
@@ -10090,9 +10084,6 @@ const setLabelMargins = function (span, size) {
10090
10084
  // Inherited line-height values can really mess up placement.
10091
10085
  $container.css("line-height", "normal");
10092
10086
 
10093
- // TODO: Verify that this change doesn't impact other graphies
10094
- // Check the full list of consuming widgets.
10095
-
10096
10087
  // If the change in line-height affected the height of the element,
10097
10088
  // then the height used for calculations should be updated.
10098
10089
  // This can happen when the first label in the container calls this method,
@@ -19087,6 +19078,9 @@ const getWidget = name => {
19087
19078
  }
19088
19079
  return widgets[name].widget;
19089
19080
  };
19081
+ const getWidgetExport = name => {
19082
+ return widgets[name] ?? null;
19083
+ };
19090
19084
  const getWidgetValidator = name => {
19091
19085
  return widgets[name]?.validator ?? null;
19092
19086
  };
@@ -19395,6 +19389,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
19395
19389
  replaceEditor: replaceEditor,
19396
19390
  replaceDeprecatedEditors: replaceDeprecatedEditors,
19397
19391
  getWidget: getWidget,
19392
+ getWidgetExport: getWidgetExport,
19398
19393
  getWidgetValidator: getWidgetValidator,
19399
19394
  getEditor: getEditor,
19400
19395
  getTransform: getTransform,
@@ -21988,16 +21983,6 @@ class InputNumber extends React__namespace.Component {
21988
21983
  currentValue: props.currentValue
21989
21984
  };
21990
21985
  }
21991
- static getOneCorrectAnswerFromRubric(rubric) {
21992
- if (rubric.value == null) {
21993
- return;
21994
- }
21995
- let answerString = String(rubric.value);
21996
- if (rubric.inexact && rubric.maxError) {
21997
- answerString += " \u00B1 " + rubric.maxError;
21998
- }
21999
- return answerString;
22000
- }
22001
21986
  shouldShowExamples = () => {
22002
21987
  return this.props.answerType !== "number";
22003
21988
  };
@@ -22156,7 +22141,17 @@ var InputNumber$1 = {
22156
22141
  widget: InputNumber,
22157
22142
  transform: propTransform$3,
22158
22143
  isLintable: true,
22159
- validator: inputNumberValidator
22144
+ validator: inputNumberValidator,
22145
+ getOneCorrectAnswerFromRubric(rubric) {
22146
+ if (rubric.value == null) {
22147
+ return;
22148
+ }
22149
+ let answerString = String(rubric.value);
22150
+ if (rubric.inexact && rubric.maxError) {
22151
+ answerString += " \u00B1 " + rubric.maxError;
22152
+ }
22153
+ return answerString;
22154
+ }
22160
22155
  };
22161
22156
 
22162
22157
  const ParseTex = TexWrangler.parseTex;
@@ -22335,34 +22330,6 @@ class NumericInput extends React__namespace.Component {
22335
22330
  currentValue: props.currentValue
22336
22331
  };
22337
22332
  }
22338
- static getOneCorrectAnswerFromRubric(rubric) {
22339
- const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22340
- const answerStrings = correctAnswers.map(answer => {
22341
- // Figure out how this answer is supposed to be
22342
- // displayed
22343
- let format = "decimal";
22344
- if (answer.answerForms && answer.answerForms[0]) {
22345
- // NOTE(johnsullivan): This isn't exactly ideal, but
22346
- // it does behave well for all the currently known
22347
- // problems. See D14742 for some discussion on
22348
- // alternate strategies.
22349
- format = answer.answerForms[0];
22350
- }
22351
-
22352
- // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'MathFormat | undefined'.
22353
- let answerString = KhanMath.toNumericString(answer.value, format);
22354
- if (answer.maxError) {
22355
- answerString += " \u00B1 " +
22356
- // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'MathFormat | undefined'.
22357
- KhanMath.toNumericString(answer.maxError, format);
22358
- }
22359
- return answerString;
22360
- });
22361
- if (answerStrings.length === 0) {
22362
- return;
22363
- }
22364
- return answerStrings[0];
22365
- }
22366
22333
  state = {
22367
22334
  // keeps track of the other set of values when switching
22368
22335
  // between 0 and finite solutions
@@ -22549,7 +22516,35 @@ var NumericInput$1 = {
22549
22516
  widget: NumericInput,
22550
22517
  transform: propsTransform,
22551
22518
  isLintable: true,
22552
- validator: numericInputValidator
22519
+ validator: numericInputValidator,
22520
+ getOneCorrectAnswerFromRubric(rubric) {
22521
+ const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22522
+ const answerStrings = correctAnswers.map(answer => {
22523
+ // Figure out how this answer is supposed to be
22524
+ // displayed
22525
+ let format = "decimal";
22526
+ if (answer.answerForms && answer.answerForms[0]) {
22527
+ // NOTE(johnsullivan): This isn't exactly ideal, but
22528
+ // it does behave well for all the currently known
22529
+ // problems. See D14742 for some discussion on
22530
+ // alternate strategies.
22531
+ format = answer.answerForms[0];
22532
+ }
22533
+
22534
+ // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'MathFormat | undefined'.
22535
+ let answerString = KhanMath.toNumericString(answer.value, format);
22536
+ if (answer.maxError) {
22537
+ answerString += " \u00B1 " +
22538
+ // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'MathFormat | undefined'.
22539
+ KhanMath.toNumericString(answer.maxError, format);
22540
+ }
22541
+ return answerString;
22542
+ });
22543
+ if (answerStrings.length === 0) {
22544
+ return;
22545
+ }
22546
+ return answerStrings[0];
22547
+ }
22553
22548
  };
22554
22549
 
22555
22550
  /**
@@ -28541,7 +28536,7 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
28541
28536
  * coordinate, and `false` uses oldCoord as the resulting coordinate.
28542
28537
  */
28543
28538
  extraCoordConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
28544
- const y = ___default["default"].head(asymptote)[1];
28539
+ const y = asymptote[0][1];
28545
28540
  return ___default["default"].all(coords, coord => coord[1] !== y);
28546
28541
  },
28547
28542
  extraAsymptoteConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
@@ -28565,7 +28560,7 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
28565
28560
  getCoefficients: function (coords, asymptote) {
28566
28561
  const p1 = coords[0];
28567
28562
  const p2 = coords[1];
28568
- const c = ___default["default"].head(asymptote)[1];
28563
+ const c = asymptote[0][1];
28569
28564
  const b = Math.log((p1[1] - c) / (p2[1] - c)) / (p1[0] - p2[0]);
28570
28565
  const a = (p1[1] - c) / Math.exp(b * p1[0]);
28571
28566
  return [a, b, c];
@@ -28592,7 +28587,7 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
28592
28587
  defaultCoords: [[0.55, 0.5], [0.75, 0.75]],
28593
28588
  defaultAsymptote: [[0.5, 0], [0.5, 1.0]],
28594
28589
  extraCoordConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
28595
- const x = ___default["default"].head(asymptote)[0];
28590
+ const x = asymptote[0][0];
28596
28591
  return ___default["default"].all(coords, coord => coord[0] !== x) && coords[0][1] !== coords[1][1];
28597
28592
  },
28598
28593
  extraAsymptoteConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
@@ -28620,10 +28615,12 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
28620
28615
  // logic between the two 'models'.
28621
28616
  const flip = coord => [coord[1], coord[0]];
28622
28617
  const inverseCoeffs = Exponential.getCoefficients(___default["default"].map(coords, flip), ___default["default"].map(asymptote, flip));
28623
- const c = -inverseCoeffs[2] / inverseCoeffs[0];
28624
- const b = 1 / inverseCoeffs[0];
28625
- const a = 1 / inverseCoeffs[1];
28626
- return [a, b, c];
28618
+ if (inverseCoeffs) {
28619
+ const c = -inverseCoeffs[2] / inverseCoeffs[0];
28620
+ const b = 1 / inverseCoeffs[0];
28621
+ const a = 1 / inverseCoeffs[1];
28622
+ return [a, b, c];
28623
+ }
28627
28624
  },
28628
28625
  getFunctionForCoeffs: function (coeffs, x, asymptote) {
28629
28626
  const a = coeffs[0],
@@ -28687,11 +28684,9 @@ const functionTypeMapping = {
28687
28684
  absolute_value: AbsoluteValue
28688
28685
  };
28689
28686
  const allTypes = ___default["default"].keys(functionTypeMapping);
28690
- function functionForType(
28691
- // TODO(jeremy): Actually `$Keys<typeof functionTypeMapping>` but that
28692
- // triggers TypeScript to require all of our Plot types to be fully typed which
28693
- // is a big amount of work/change.
28694
- type) {
28687
+ function functionForType(type) {
28688
+ // @ts-expect-error: TypeScript doesn't know how to use deal with generics
28689
+ // and conditional types in this way.
28695
28690
  return functionTypeMapping[type];
28696
28691
  }
28697
28692
  const getEquationString = props => {
@@ -28756,12 +28751,13 @@ const defaultPlotProps = (type, graph) => {
28756
28751
  // widget before even reading the question; you can't lose, but you
28757
28752
  // might get a free win.
28758
28753
  const model = functionForType(type);
28754
+ const defaultAsymptote = "defaultAsymptote" in model ? model.defaultAsymptote : null;
28759
28755
  const gridStep = [1, 1];
28760
28756
  // @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type '[number, number]'.
28761
28757
  const snapStep = Util.snapStepFromGridStep(gridStep);
28762
28758
  return {
28763
28759
  type,
28764
- asymptote: maybePointsFromNormalized(model.defaultAsymptote, graph.range, graph.step, snapStep),
28760
+ asymptote: maybePointsFromNormalized(defaultAsymptote, graph.range, graph.step, snapStep),
28765
28761
  coords: null
28766
28762
  };
28767
28763
  };
@@ -28821,8 +28817,19 @@ var util = /*#__PURE__*/Object.freeze({
28821
28817
  typeToButton: typeToButton
28822
28818
  });
28823
28819
 
28824
- function grapherValidator(state, rubric) {
28825
- if (state.type !== rubric.correct.type) {
28820
+ function getCoefficientsByType(data) {
28821
+ if (data.type === "exponential" || data.type === "logarithm") {
28822
+ const grader = functionForType(data.type);
28823
+ return grader.getCoefficients(data.coords, data.asymptote);
28824
+ } else if (data.type === "linear" || data.type === "quadratic" || data.type === "absolute_value" || data.type === "sinusoid" || data.type === "tangent") {
28825
+ const grader = functionForType(data.type);
28826
+ return grader.getCoefficients(data.coords);
28827
+ } else {
28828
+ throw new perseusCore.PerseusError("Invalid grapher type", perseusCore.Errors.InvalidInput);
28829
+ }
28830
+ }
28831
+ function grapherValidator(userInput, rubric) {
28832
+ if (userInput.type !== rubric.correct.type) {
28826
28833
  return {
28827
28834
  type: "points",
28828
28835
  earned: 0,
@@ -28832,7 +28839,7 @@ function grapherValidator(state, rubric) {
28832
28839
  }
28833
28840
 
28834
28841
  // We haven't moved the coords
28835
- if (state.coords == null) {
28842
+ if (userInput.coords == null) {
28836
28843
  return {
28837
28844
  type: "invalid",
28838
28845
  message: null
@@ -28840,11 +28847,9 @@ function grapherValidator(state, rubric) {
28840
28847
  }
28841
28848
 
28842
28849
  // Get new function handler for grading
28843
- const grader = functionForType(state.type);
28844
- const guessCoeffs = grader.getCoefficients(state.coords, state.asymptote);
28845
- const correctCoeffs = grader.getCoefficients(rubric.correct.coords,
28846
- // @ts-expect-error - TS(2339) - Property 'asymptote' does not exist on type '{ type: "absolute_value"; coords: [Coord, Coord]; }'
28847
- rubric.correct.asymptote);
28850
+ const grader = functionForType(userInput.type);
28851
+ const guessCoeffs = getCoefficientsByType(userInput);
28852
+ const correctCoeffs = getCoefficientsByType(rubric.correct);
28848
28853
  if (guessCoeffs == null || correctCoeffs == null) {
28849
28854
  return {
28850
28855
  type: "invalid",
@@ -37937,7 +37942,7 @@ function shouldUseMafs(mafsFlags, graph) {
37937
37942
  // TODO(benchristel): add a feature flag for the "unlimited"
37938
37943
  // case once we've implemented polygon graphs with unlimited
37939
37944
  // sides
37940
- return false;
37945
+ return Boolean(mafsFlags["unlimited-polygon"]);
37941
37946
  }
37942
37947
  return Boolean(mafsFlags["polygon"]);
37943
37948
  default:
@@ -42411,17 +42416,23 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
42411
42416
  const results = [];
42412
42417
 
42413
42418
  // For convenience, extract some props into separate variables
42414
- const range = props.range;
42415
- const labelRange = props.labelRange;
42419
+ const {
42420
+ range,
42421
+ labelRange,
42422
+ labelStyle,
42423
+ labelTicks,
42424
+ tickStep,
42425
+ numDivisions
42426
+ } = props;
42416
42427
  const leftLabel = labelRange[0] == null ? range[0] : labelRange[0];
42417
42428
  const rightLabel = labelRange[1] == null ? range[1] : labelRange[1];
42418
42429
 
42419
42430
  // Find base via GCD for non-reduced fractions
42420
42431
  let base;
42421
- if (props.labelStyle === "non-reduced") {
42432
+ if (labelStyle === "non-reduced") {
42422
42433
  const fractions = [leftLabel, rightLabel];
42423
- for (let i = 0; i <= props.numDivisions; i++) {
42424
- const x = range[0] + i * props.tickStep;
42434
+ for (let i = 0; i <= numDivisions; i++) {
42435
+ const x = range[0] + i * tickStep;
42425
42436
  fractions.push(x);
42426
42437
  }
42427
42438
  const getDenom = x => kmath.number.toFraction(x)[1];
@@ -42430,30 +42441,40 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
42430
42441
  } else {
42431
42442
  base = undefined;
42432
42443
  }
42433
-
42434
- // Draw and save the tick marks and tick labels
42435
- for (let i = 0; i <= props.numDivisions; i++) {
42436
- const x = range[0] + i * props.tickStep;
42437
- results.push(graphie.line([x, -0.2], [x, 0.2]));
42438
- const labelTicks = props.labelTicks;
42439
- if (labelTicks || props.labelStyle === "decimal ticks") {
42440
- if (x === leftLabel || x === rightLabel) {
42441
- results.push(graphie.style({
42442
- color: KhanColors.BLUE
42443
- }, () => _label(graphie, props.labelStyle, x, x, base)));
42444
- } else {
42445
- results.push(_label(graphie, props.labelStyle, x, x, base));
42446
- }
42447
- }
42448
- }
42449
-
42450
- // Render the labels' lines
42451
- graphie.style({
42444
+ const highlightedLineStyle = {
42452
42445
  stroke: KhanColors.BLUE,
42453
42446
  strokeWidth: 3.5
42454
- }, () => {
42455
- results.push(graphie.line([leftLabel, -0.2], [leftLabel, 0.2]));
42456
- results.push(graphie.line([rightLabel, -0.2], [rightLabel, 0.2]));
42447
+ };
42448
+ const highlightedTextStyle = {
42449
+ color: KhanColors.BLUE
42450
+ };
42451
+
42452
+ // Generate an array of tick numbers:
42453
+ // `Array(props.numDivisions)` makes an array of null values - one for every division marker
42454
+ // `.keys()` gets the index values for each marker placeholder
42455
+ // `.map()` converts the index values into actual tick numbers
42456
+ const initialTicks = [...Array(numDivisions).keys()].map(index => range[0] + index * tickStep);
42457
+
42458
+ // .sort() comparator
42459
+ const byNumericAscending = (a, b) => a - b;
42460
+
42461
+ // Ensure that any label markers and range endpoints are included in the array
42462
+ // Using `Set()` prevents duplication of tick numbers (and is quite performant)
42463
+ const allTicks = [...new Set([...initialTicks, leftLabel, rightLabel, ...range])].sort(byNumericAscending);
42464
+
42465
+ // Cycle through each tick number and add a tick line, and a label (if needed)
42466
+ allTicks.forEach(tick => {
42467
+ const tickIsHighlighted = tick === leftLabel || tick === rightLabel;
42468
+ const lineStyle = tickIsHighlighted ? highlightedLineStyle : null;
42469
+ const textStyle = tickIsHighlighted ? highlightedTextStyle : null;
42470
+ graphie.style(lineStyle, () => {
42471
+ results.push(graphie.line([tick, -0.2], [tick, 0.2]));
42472
+ });
42473
+ if (labelTicks || tickIsHighlighted || labelStyle === "decimal ticks") {
42474
+ graphie.style(textStyle, () => {
42475
+ results.push(_label(graphie, labelStyle, tick, tick, base));
42476
+ });
42477
+ }
42457
42478
  });
42458
42479
  return results;
42459
42480
  });
@@ -43147,7 +43168,7 @@ class Orderer extends React__namespace.Component {
43147
43168
  list.splice(index, 0, newCard);
43148
43169
  }
43149
43170
  this.props.onChange({
43150
- // @ts-expect-error - TS2345 - Argument of type '{ current: any[]; }' is not assignable to parameter of type '{ hints?: readonly Hint[] | undefined; replace?: boolean | undefined; content?: string | undefined; widgets?: WidgetDict | undefined; images?: ImageDict | undefined; ... 13 more ...; plot?: any; }'.
43171
+ // @ts-expect-error - TS2345 - Argument of type '{ current: any[]; }' is not assignable to parameter of type '{ hints?: readonly Hint[] | undefined; replace?: boolean | undefined; content?: string | undefined; widgets?: PerseusWidgetsMap | undefined; images?: ImageDict | undefined; ... 13 more ...; plot?: any; }'.
43151
43172
  current: list
43152
43173
  });
43153
43174
  this.setState({
@@ -43310,7 +43331,7 @@ class Orderer extends React__namespace.Component {
43310
43331
  };
43311
43332
  });
43312
43333
  this.props.onChange({
43313
- // @ts-expect-error - TS2345 - Argument of type '{ current: { content: string; }[]; }' is not assignable to parameter of type '{ hints?: readonly Hint[] | undefined; replace?: boolean | undefined; content?: string | undefined; widgets?: WidgetDict | undefined; images?: ImageDict | undefined; ... 13 more ...; plot?: any; }'.
43334
+ // @ts-expect-error - TS2345 - Argument of type '{ current: { content: string; }[]; }' is not assignable to parameter of type '{ hints?: readonly Hint[] | undefined; replace?: boolean | undefined; content?: string | undefined; widgets?: PerseusWidgetsMap | undefined; images?: ImageDict | undefined; ... 13 more ...; plot?: any; }'.
43314
43335
  current: list
43315
43336
  });
43316
43337
  this.setState({
@@ -45158,7 +45179,7 @@ const init = function (options) {
45158
45179
 
45159
45180
  // This file is processed by a Rollup plugin (replace) to inject the production
45160
45181
  const libName = "@khanacademy/perseus";
45161
- const libVersion = "37.0.0";
45182
+ const libVersion = "38.0.0";
45162
45183
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45163
45184
 
45164
45185
  const apiVersion = {
@@ -48417,9 +48438,7 @@ const getCorrectAnswerForWidgetId = (widgetId, itemData) => {
48417
48438
  const rubric = itemData.question.widgets[widgetId].options;
48418
48439
  const widgetMap = getWidgetsMapFromItemData(itemData);
48419
48440
  const widgetType = getWidgetTypeByWidgetId(widgetId, widgetMap);
48420
- const widget = getWidget(widgetType);
48421
-
48422
- // @ts-expect-error - TS2339 - Property 'getOneCorrectAnswerFromRubric' does not exist on type 'ComponentType<any>'.
48441
+ const widget = getWidgetExport(widgetType);
48423
48442
  return widget?.getOneCorrectAnswerFromRubric?.(rubric);
48424
48443
  };
48425
48444